/*
 * K.I.S.S. Konsole for High Performance Software Defined Radio
 *
 * Developed from original code  Copyright 2006 (C) Phil Covington, N8VB
 * 
 * Copyright (C) 2009 Phil Harman, VK6APH
 * Copyright (C) 2009 David McQuate WA8YWQ
 * Copyright (C) 2009 Joe Martin K5SO
 * Copyright (C) 2009 George Byrkit K9TRV
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * 
 */

/*
 * Change log 
 * 
 *  1 June 2009 - Released as V1.0.0
 *  4 June 2009 - Allow keypad frequency entry with period for MHz
 *              - Changed frequency display to MHz.kHz Hz
 *  5 June 2009 - Improve Grid code and show all band edges in red
 *              - Fixed large memory size
 *  6 June 2009 - Make floats region independant (',' instead of '.')
 *              - Fixed bug in mouse frequency drag code.
 * 10 June 2009 - Added split display if both wide and narrow bandscopes being displayed
 * 11 June 2009 - Release as V1.0.1
 * 12 June 2009 - Add improved KK.csv file handling by George K9TRV
 *              - Change mouse cursor when over bandscope and when left mouse button down
 * 13 June 2009 - Read mouse speed and if necessary slow it down when drag tuning
 *              - S Meter test code from George K9TRV
 *              - Added mouse click tune
 *              - Working on mouse setting of the filter bandwidth
 * 14 June 2009 - fixed bug in drag tune rate - thanks Lyle KK7P for spotting!
 *              - Removed mouse sensitivity test since no longer needed
 *              - Added bandpass shift using mouse - WIP 
 *              - Added setting filter high and low frequencies with mouse -WIP
 * 15 June 2009 - Performance tuning by George K9TRV
 *              - More SMeter code by K9TRV
 *              - Limit bandpass shift to +/-9.999kHz
 *              - Limit filter drag to +/- 9.999kHz
 * 17 June 2009 - Textural SMeter working, fixed bug in click tune - both from George K9TRV
 * 20 June 2009 - Added Graphical S Meter
 *              - Standard calibrations added - will need a user control for fine calibration.
 * 21 June 2009 - Improvements to SMeter code, only draw background once - K9TRV
 *              - Added AGC hang time control.
 *              - Started contols for max, min and step settings for bandscope, still WIP
 * 22 June 2009 - Fixed bug in keyboard entry that cleared frequency display - K1RQG spotted this!
 *              - Added users settings for ANF and NR - K9TRV
 *              - Removed need to redraw bandscope backgound each update - K9TRV
 *              - Further work on max,min and step controls  - WIP
 * 24 June 2009 - Added FM and SAM modes - K9TRV
 *              - Completed bandscope controls, settings saved to KK.csv
 * 25 June 2009 - Improved bandscope drawing - K9TRV
 *              - Updated bandscope calibration description
 *              - Added bandwidth controls for SAM and FM
 *              - Increased max filter width in CW mode to 1kHz
 * 11 July 2009 - Send C&C data to Ozy if nothing received from EP6 to force it to send data
 *              - Setup now tool strip item so will open and close correctly - Harman Jnr
 *                good to see all those college $'s were well spent!
 *              - Release as V1.0.2
 * 12 July 2009 - Start of Waterfall display or how to use all your CPU in a few lines of code!
 * 13 July 2009 - Quick hack to correct click tune for CWL - needs more work 
 * 14 July 2009 - More Waterfall work - much less CPU - K9TRV
 *              - Added colours for the different waterfall levels
 * 16 July 2009 - Added seperate pictureboxs for each display - K9TRV
 *              - Added adjustable size for bandscope windows - K9TRV
 * 17 July 2009 - Added automatic setting for Waterfall low colour i.e. 'Waterfall AGC'
 * 18 July 2009 - Release as V1.0.3
 *              - Added squelch control for bandscope
 *              - Added squelch control for filter bandwidth
 *              - Added background colour for squelch control to show status
 * 23 July 2009 - Reduced CPU load by changing ADC Overload LED method - thanks Willi SM6OMH for spotting this!
 * 25 July 2009 - Added GUI for squelch level control and compenstated for filter BW
 * 26 July 2009 - Release as V1.0.4
 * 29 July 2009 - Merged Penelope code from Dave, WA8YWQ
 * 30 July 2009 - Added Hardware configuration options and selection for Penelope
 *              - Added 20dB Mic Gain option for Penelope
 *              - Added Hardware selection for Excalibur & save setting in KK.csv
 *              - Added 10MHz clock selection & save setting in KK.csv
 * 31 July 2009 - Added Tabs to Setup form - K9TRV
 *              - Added Tx filter high and low selection and save in KK.csv
 *              - Added Tx drive control and save in KK.csv
 *              - Added Tx gain per band to setup form and save values in KK.csv
 *  1 Aug  2009 - Added Full Duplex option and save setting in KK.csv
 *              - Added MOX buttton, mute receiver audio if not full duplex
 *              - Added Tune button and % power setting on setup form and save in KK.csv
 *                  TODO: Cancel MOX/TUN if active and Start clicked
 *  2 Aug  2009 - Added CW Pitch control to setup, filter settings and save values in KK.csv
 *              - Fixed bug when opening setup form - K9TRV
 *              - Started adding J6 output controls for Penelope
 *              - Added PTT from Atlas Bus
 *  3 Aug  2009 - PTT from Atlas immediately sent back via C&C C0 to minimise delays
 *              - Added Penny Open Collector control group enable/disable and save in KK.csv
 *              - More code for Penny Open Collector controls
 *  4 Aug  2009 - Yet more code for Penny Open Collector controls - who's bright idea was it to have 154? :-)
 *  6 Aug  2009 - Completed Penny Open Collect controls - K9TRV
 *              - Added Penny Open Collector controls to C&C data
 *              - Replaced Start check box with ON/OFF button 
 *              - Interlocked ON/OFF with MOX and TUN controls
 *  7 Aug  2009 - Added independant Duplex controls for Tx and Rx
 *              - Code review and improvements - K9TRV
 *  8 Aug  2009 - Release as V1.0.5
 *
 *  
 *              
 *    
 * 
 * TODO:        - Investigate CPU useage when ANF/NR on at 192kHz sampling rate
 *              - Change USB buffer sizes as a function of sampling rate i.e. 
 *                    WriteBuf size = 1024 bytes for samples/block < 1024, 2048 otherwise
 *                    ReadBuf size = WriteBuf size * (1,2,4) for (48k, 96k, 192k) sample rates
 *              - Check 1Hz steps with mouse scoll is OK with other mice.
 *              - Improve AGC performance 
 *              - Add user AGC mode (Done) and allow parameters to be adjusted 
 *              - Review CPU usage of ANF and NR values 
 *              - Add  multi band EQ for the receiver (like the 10 band one in PowerSDR)
 *              - User calibration of frequency and amplitude
 *              - Add a second receiver within the current bandwidth
 *              - Fix click tune for CWL - need to allow for LSB filter and CW tone frequency
 *              
 *
 * 
 * 
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using HPSDR_USB_LIB_V1;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using SharpDSP;
using System.Diagnostics;   // use View > Output to see debug messages
using System.Globalization;  // so we don't have country issues with '.' and ',' in KK.csv files
using System.Net;
using System.Net.Sockets;
using System.IO.Pipes;
using System.Security.Principal;

namespace KISS_Konsole
{
    public partial class Form1 : Form
    { 
        string version = "V1.0.5";  // change this for each release!
        string Ozy_version = null;  // holds version of Ozy code loaded into FX2
        public string temp = "";
        public string output_frequency = "";
        public string output_band = "";
        public string output_signcall = "";
        
       
        public String receive = ""; //receive message
        TcpListener tcpListener = new TcpListener(IPAddress.Any, 4444); //tcp listener for SDT
       
        public Boolean IsGetBand = false;
        public Boolean IsChanged = false;
       

        const int isize = 1024;
        const int qsize = 1024;
        IntPtr hdev = IntPtr.Zero;          // USB Ozy device handle
        byte[] rbuf = new byte[2048];       // Ozy EP6 samples read buffer for real-time receiver
        byte[] EP4buf = new byte[8192];     // Ozy EP4 samples read buffer for full-bandwidth spectrum display
        byte[] frequency = new byte[4];
        byte rc0, rc1, rc2, rc3, rc4;       // Command & Control bytes received from Ozy

        public SharpDSP2._1.DSPState state;  // public so that the Setup form can use it
        SharpDSP2._1.DSPBuffer SignalBuffer;
        public SharpDSP2._1.Receiver rcvr;   // public so that the Setup form can use it 
        SharpDSP2._1.RingBuffer AudioRing;
        float[] PowerSpectrumData = new float[2048];    // data acquired from rcvr.PowerSpectrumSignal event

        // for full bandwidth power spectrum display--
        SharpDSP2._1.DSPState FullBandwidthSpectrumState;
        SharpDSP2._1.DSPBuffer FullBandwidthSpectrumBuffer;
        SharpDSP2._1.OutbandPowerSpectrumSignal FullBandwidthSpectrum;
        float[] FullBandwidthPowerSpectrumData = new float[8192];

       // Provide a filter for SSB transmit
        SharpDSP2._1.DSPState TransmitFilterState;
        SharpDSP2._1.DSPBuffer TransmitFilterBuffer;
        SharpDSP2._1.Filter TransmitFilter;
        SharpDSP2._1.RingBuffer TransmitAudioRing;
        int MicTestSample = 0;
        int step_size; // the frequency step using the mouse scroll wheel
        bool read_ready; // set when we have read 1024 samples from USB port
        int[] I_sample = new int[isize]; // holds the I samples from Ozy
        int[] Q_sample = new int[isize]; // holds the Q samples from Ozy
 
        int MicSampleIn = 0;                        // with input
        int MicSample = 0;                          // Counter used to decimate Mic samples when EP6 sample rate is > 48k
        
        short[] CWnoteSin = new short[80];      // An array of samples for baseband CW note;
        short[] CWnoteCos = new short[80];
        int CWnoteIndex = 0;
        public string ModeText;
        int sample_no = 0;
        const byte sync = 0x7F;
        int previous_value;
        byte C0 = 0x00; byte C1, C2, C3, C4;
        public int SampleRate;
        int SampleCounter = 0;      // used to pace calls to MercurySend() in ProcessData512 when SampleRate > 48000
        public int CWPitch = 700;   // used to calculate filter center frequency, mouse tuning, etc.

        // the following contain the last used VFO frequency on each band
        int set_frequency_160, set_frequency_80, set_frequency_40, set_frequency_30, set_frequency_20;
        int set_frequency_17, set_frequency_15, set_frequency_12, set_frequency_10, set_frequency_6, set_frequency_GC;
        // the following hold the last state of the preamp on each band
        bool Preamp_160, Preamp_80, Preamp_40, Preamp_30, Preamp_20, Preamp_17, Preamp_15, Preamp_12, Preamp_10;
        bool Preamp_6, Preamp_GC;

        string BandText; // holds the previous band when we change bands so we can restore the frequency we were on
        bool PTT = false;           // true when PTT on Atlas is active
        bool USB_thread_running = false;

        int Merc_version;           // Version number of Mercury FPGA code
        int Penny_version;          // Version number of Penelope FPGA code
        int Ozy_FPGA_version;       // Version number of Ozy FPGA code 

        public int Rate;            // sets bandscope update rate in F.P.S.

        public bool updateSpectrumGraphBackground = true;
        public bool updateWidebandGraphBackground = true;

        private bool showI;          // when set displays I chanel on 'scope
        public bool ShowI
        {
            get { return showI; }
            set { showI = value; SetSplitterBar(); }
        }

        //Start to listenint the port 4444
        //This way SDT application will be connected to Kiss Konsol
        public void listenerStart()
        {

            tcpListener.Start();

        }
        private bool showQ;          // when set displays Q chanel on 'scope

        public bool ShowQ
        {
            get { return showQ; }
            set { showQ = value; SetSplitterBar(); }
        }

        private void SetSplitterBar()
        {
            if ((chkSpec.Checked || ShowI || ShowQ) && (chkWaterFall.Checked || chkWideSpec.Checked))
            {
                // multi-part display.  If not yet multi-part, change the splitter point
                if (splitContainer1.IsSplitterFixed)
                {
                   splitContainer1.SplitterDistance = 256;
                }
               if (chkWaterFall.Checked)
               {
                    // waterfall is exactly 256 lines.  don't allow splitter bar to move
                    splitContainer1.IsSplitterFixed = true;
                    splitContainer1.SplitterDistance = 256;
                }
                else
                {
                    // allow user to choose how much wideband and how much spectrum
                    splitContainer1.IsSplitterFixed = false;
                }

                UpdateGraphBackgrounds();
            }
            else if (chkSpec.Checked || ShowI || ShowQ)
            {
                // top display only
                splitContainer1.SplitterDistance = 512;
                splitContainer1.IsSplitterFixed = true;
                UpdateGraphBackgrounds();
            }
            else if (chkWaterFall.Checked || chkWideSpec.Checked)
            {
                // bottom display only
                splitContainer1.SplitterDistance = 0;
                splitContainer1.IsSplitterFixed = true;
                UpdateGraphBackgrounds();
            }
        }

        public int IQScale = 6;     // sets default display scale of I and Q signals

        public int GridMax = 0;     // sets maximum value of Bandscope display
        public int GridMin = -160;  // sets minimum value of Bandscope display
        public int GridStep = 20;   // sets distance between grid lines on bandscope 

        public int WaterfallStep = 0; // sets the distance in pixels the line moves per Waterfall scan

        int delta;                  // difference between the current and previous mouse position on the bandscope
        int loop_count = 0;         // delay until we read FPGA code version from Ozy
        int QuickMemory;            // holds current Quick Memory frequency
        string QuickMemoryBand;     // holds the current Quick Memory band
        public string Your_call;           // holds the users call sign
        string KeypadFrequency = null;     // holds the frequency entered using the keypad
        int keycount = 0;           // holds number of key presses of the  keypad
        bool ShowCross = false;     // when set show cross at current mouse location
        int MousePositionX;         // as it says
        int MousePositionY;         // you guessed it!
        int FilterLeft;             // left hand location of filter on bandscope
        int FilterRight;            // Hmm... now let me see...
        bool FrequencyDrag = true;  // true when we can drag tune with the mouse
        bool SquelchDrag = false;   // true when you can drag the squelch level 
        bool BandwidthShift = true; // true when can drag shift the filter
        bool HighFilter = false;    // true when we can drag high filter frequency
        bool LowFilter = false;     // true when we can drag low filter frequency
        public bool WaterfallAGC = false;  // true when Waterfall AGC is selected on the Setup form
        int max_signal = -200;      // maximum value of nPS in the array, used by  bandscope squelch
        int filter_max_signal = -200;  // maximum signal through the current filter, used by the filter, squelch
        int pixels_per_GridStep;     // how many pixels a Grid Step represents in the narrow bandscope 
        bool IsSynced = false;      // true when we have sync from Ozy
        bool ADCOverload = false;     // true when ADC is overloaded
        bool KK_on = false;           // true when KK is running

        int squelch_y_value = 0;      // Y location of the squelch threshold line 
        Rectangle squelch = new Rectangle();  // the little rectangle at the LH end of the squelch line.

        public bool PenneyPresent = false;  // true if a Penelope card is fitted or not
        public bool MicGain20dB = false;    // true if 20dB mic gain is active on Penny
        public bool Penelope10MHz = false;  // true if 10MHz reference comes from Penny
        public bool Atlas10MHz = false;     // true if 10MHz reference comes from Atlas or Excalibur
        public bool Mercury10MHz = false;   // true if 10MHz reference comes from Mercury
        public bool Excalibur = false;      // true if Excalibur card fitted
        public float TxFilterHigh = 3500f;  // Transmitter high filter setting
        public float TxFilterLow = 300f;    // Transmitter low filter setting
        float DriveGain = 0.50f;            // Determines the drive level to Penelope
        public int Gain160m = 50;           // Determines gain for Penelope on 160m
        public int Gain80m = 50;            // ditto for 80m etc
        public int Gain60m = 50;
        public int Gain40m = 50;
        public int Gain30m = 50;
        public int Gain20m = 50;
        public int Gain17m = 50;
        public int Gain15m = 50;
        public int Gain12m = 50;
        public int Gain10m = 50;
        public int Gain6m  = 50;
        public int BandGain = 50;           // Determines gain for Penelope, set by what ever band selected

        public bool Duplex = false;         // Selects full duplex operation
        public bool OnlyTxOnPTT = true;     // part of full duplex, only Tx when PTT set
        bool MOX_set = false;               // set when PTT active
        bool TUN_set = false;               // set when Tune active
        public int TuneLevel = 50;          // sets Tune power as a % of Drive setting
        bool Previous_PTT = false;          // holds the previous PTT state so we can determine when its changed
        bool previous_OnlyTxOnPTT;          // hold previous Only Tx on PTT start so we can restore 
        // Penny Open Collector settings 
        public bool PennyOC = false;        // when true enables Penny OC settings
        public int Penny160mTxOC = 0;
        public int Penny160mRxOC = 0;
        public int Penny80mTxOC = 0;
        public int Penny80mRxOC = 0;
        public int Penny60mTxOC = 0;
        public int Penny60mRxOC = 0;
        public int Penny40mTxOC = 0;
        public int Penny40mRxOC = 0;
        public int Penny30mTxOC = 0;
        public int Penny30mRxOC = 0;
        public int Penny20mTxOC = 0;
        public int Penny20mRxOC = 0;
        public int Penny17mTxOC = 0;
        public int Penny17mRxOC = 0;
        public int Penny15mTxOC = 0;
        public int Penny15mRxOC = 0;
        public int Penny12mTxOC = 0;
        public int Penny12mRxOC = 0;
        public int Penny10mTxOC = 0;
        public int Penny10mRxOC = 0;
        public int Penny6mTxOC = 0;
        public int Penny6mRxOC = 0;

        private Thread USB_thread;	// runs the loop facility 

        Point MouseOld = new Point(); // keep location of mouse for drag tuning 

        public SetupForm Setup_form = null;  // get reference to the Setup form.

        SharpDSP2._1.CPX[] outbuffer = new SharpDSP2._1.CPX[1024];     // allocate a buffer for DSP output

        NumberFormatInfo nfi = NumberFormatInfo.InvariantInfo;  // so we are region independant in terms of ',' and '.' for floats
        // these are used by the SMeter 
        Rectangle Meter;
        int SMeterValue = 0;          // this gets passed to the SMeter code 
        Font meterFont = new Font(FontFamily.GenericSansSerif, 7);  // use smaller font for the meter

        Image SMeterBackgroundImage;
        #region

        private void CreateKKCSV()
        {
            #region The contents of KK.csv
            /*  contents of a KK.CSV default file
             * 
                Users Call,your call here
                Band,40m
                Sample Rate,48000
                Step Size,10Hz
                Mode,USB
                Volume,8
                AGC-T,43
                AGC Speed,Long
                Bandwidth,3382
                Last Freq 160m,1836056
                Last Freq 80m,3635928
                Last Freq 40m,7135010
                Last Freq 30m,10120082
                Last Freq 20m,14266100
                Last Freq 17m,18091270
                Last Freq 15m,21045856
                Last Freq 12m,24950010
                Last Freq 10m,28040534
                Last Freq 6m,50257000
                Last Freq GC,16998470
                Last frequency,14266100
                ANF,False
                NR,False
                NB1,False
                NB2,False
                NB1 Threshold,8.8
                NB2 Threshold,9
                Bandscope Average,True
                Bandscope Smooth,0.6
                Rate,15
                Preamp 160,False
                Preamp 80,False
                Preamp 40,False
                Preamp 30,False
                Preamp 20,False
                Preamp 17,True
                Preamp 15,False
                Preamp 12,False
                Preamp 10,False
                Preamp 6,False
                Preamp GC,False
             * 
             */
            #endregion

            // set all data accordingly
            Your_call = "your call here";
            BandSelect.Text = "40m";
            SampleRate = 48000;
            stepSize.Text = "10Hz";
            Mode.Text = "USB";
            VolumeTrackBar.Value = 8;
            AGCTrackBar.Value = 43;
            AGCSpeed.Text = "Long";
            BandwidthTrackBar.Value = 3382;
            set_frequency_160 = 1836056;
            set_frequency_80 = 3635928;
            set_frequency_40 = 7135010;
            set_frequency_30 = 10120082;
            set_frequency_20 = 14266100;
            set_frequency_17 = 18091270;
            set_frequency_15 = 21045856;
            set_frequency_12 = 24950010;
            set_frequency_10 = 28040534;
            set_frequency_6 = 50257000;
            set_frequency_GC = 16998470;
            set_frequency.Value = 14266100;
            ANF.Checked = false;
            NR.Checked = false;
            NB1.Checked = false;
            NB2.Checked = false;
            rcvr.BlockNBThreshold = 8.8F;
            rcvr.AveNBThreshold = 9.0F;
            rcvr.PowerSpectrumAveragingOn = true;
            rcvr.PowerSpectrumSmoothingFactor = 0.6F;
            Rate = 15;
            Preamp_160 = false;
            Preamp_80 = false;
            Preamp_40 = false;
            Preamp_30 = false;
            Preamp_20 = false;
            Preamp_17 = false;
            Preamp_15 = false;
            Preamp_10 = false;
            Preamp_6 = false;
            Preamp_GC = false;
            output_band = "40m";
            // create the file
            WriteKKCSV();
        }

        private void WriteKKCSV()
        {
            // save the current settings to KK.csv
            List<string> lines = new List<string>();        // create a List<string> to hold all the output lines

            // each line of the output file, in output order
            lines.Add("Users Call," + Your_call);
            lines.Add("Band," + BandSelect.Text);
            lines.Add("Sample Rate," + SampleRate);
            lines.Add("Step Size," + stepSize.Text);
            lines.Add("Mode," + Mode.Text);
            lines.Add("Volume," + VolumeTrackBar.Value);
            lines.Add("AGC-T," + AGCTrackBar.Value);
            lines.Add("AGC Speed," + AGCSpeed.Text);
            lines.Add("Bandwidth," + BandwidthTrackBar.Value);
            lines.Add("Last Freq 160m," + set_frequency_160);
            lines.Add("Last Freq 80m," + set_frequency_80);
            lines.Add("Last Freq 40m," + set_frequency_40);
            lines.Add("Last Freq 30m," + set_frequency_30);
            lines.Add("Last Freq 20m," + set_frequency_20);
            lines.Add("Last Freq 17m," + set_frequency_17);
            lines.Add("Last Freq 15m," + set_frequency_15);
            lines.Add("Last Freq 12m," + set_frequency_12);
            lines.Add("Last Freq 10m," + set_frequency_10);
            lines.Add("Last Freq 6m," + set_frequency_6);
            lines.Add("Last Freq GC," + set_frequency_GC);
            lines.Add("Last frequency," + set_frequency.Value);
            // we need to store the following controls as true or false, first we convert from bool to a string
            lines.Add("ANF," + (ANF.Checked).ToString());
            lines.Add("NR," + (NR.Checked).ToString());
            lines.Add("NB1," + (NB1.Checked).ToString());
            lines.Add("NB2," + (NB2.Checked).ToString());
            lines.Add("NB1 Threshold," + rcvr.BlockNBThreshold.ToString(nfi)); // use nfi to make floats region independant 
            lines.Add("NB2 Threshold," + rcvr.AveNBThreshold.ToString(nfi));
            lines.Add("Bandscope Average," + rcvr.PowerSpectrumAveragingOn.ToString());
            lines.Add("Bandscope Smooth," + rcvr.PowerSpectrumSmoothingFactor.ToString(nfi));
            lines.Add("Rate," + Rate.ToString());
            // save the preamp setting for each band
            lines.Add("Preamp 160," + Preamp_160.ToString());
            lines.Add("Preamp 80," + Preamp_80.ToString());
            lines.Add("Preamp 40," + Preamp_40.ToString());
            lines.Add("Preamp 30," + Preamp_30.ToString());
            lines.Add("Preamp 20," + Preamp_20.ToString());
            lines.Add("Preamp 17," + Preamp_17.ToString());
            lines.Add("Preamp 15," + Preamp_15.ToString());
            lines.Add("Preamp 12," + Preamp_12.ToString());
            lines.Add("Preamp 10," + Preamp_10.ToString());
            lines.Add("Preamp 6," + Preamp_6.ToString());
            lines.Add("Preamp GC," + Preamp_GC.ToString());
            // end of original kk.csv file. beginning of values that LookupKKCSVValue should be used on
            lines.Add("ANFAdaptiveFilterSize," + ANFAdaptiveFilterSize.ToString(nfi));
            lines.Add("ANFDelay," + ANFDelay.ToString(nfi));
            lines.Add("ANFAdaptationRate," + ANFAdaptationRate.ToString(nfi));
            lines.Add("ANFLeakage," + ANFLeakage.ToString(nfi));
            lines.Add("NRAdaptiveFilterSize," + NRAdaptiveFilterSize.ToString(nfi));
            lines.Add("NRDelay," + NRDelay.ToString(nfi));
            lines.Add("NRAdaptationRate," + NRAdaptationRate.ToString(nfi));
            lines.Add("NRLeakage," + NRLeakage.ToString(nfi));
            lines.Add("Bandscope Grid Max," + GridMax.ToString(nfi));
            lines.Add("Bandscope Grid Min," + GridMin.ToString(nfi));
            lines.Add("Bandscope Grid Step," + GridStep.ToString(nfi));

            lines.Add("AGC Hang Threshold," + AGCHangThreshold.ToString(nfi));
            lines.Add("AGC Hang Time," + AGCHangTime.ToString(nfi));
            lines.Add("AGC Max Gain," + AGCMaximumGainDB.ToString(nfi));
            lines.Add("AGC Slope," + AGCSlope.ToString(nfi));
            lines.Add("AGC Method," + Convert.ToInt32(AgcMethod).ToString(nfi));
            lines.Add("AGC Attack Time," + AGCAttackTime.ToString(nfi));

            lines.Add("WaterfallHigh," + WaterfallHighThreshold.ToString(nfi));
            lines.Add("WaterfallLow," + WaterfallLowThreshold.ToString(nfi));
            lines.Add("WaterFall AGC," + WaterfallAGC.ToString(nfi));

            lines.Add("Squelch level," + Squelch_level.Value.ToString(nfi));

            lines.Add("Penny Present," + PenneyPresent.ToString(nfi));
            lines.Add("Mic Gain 20dB," + MicGain20dB.ToString(nfi));
            lines.Add("Atlas 10MHz," + Atlas10MHz.ToString(nfi));
            lines.Add("Mercury 10MHz," + Mercury10MHz.ToString(nfi));
            lines.Add("Penelope 10MHz," + Penelope10MHz.ToString(nfi));
            lines.Add("Excalibur Present," + Excalibur.ToString(nfi));

            lines.Add("Tx Filter High," + TxFilterHigh.ToString(nfi));
            lines.Add("Tx Filter Low," + TxFilterLow.ToString(nfi));

            lines.Add("Drive Level," + DriveLevel.Value.ToString(nfi));

            lines.Add("Band Gain 160m," + Gain160m.ToString(nfi));
            lines.Add("Band Gain 80m," + Gain80m.ToString(nfi));
            lines.Add("Band Gain 60m," + Gain60m.ToString(nfi));
            lines.Add("Band Gain 40m," + Gain40m.ToString(nfi));
            lines.Add("Band Gain 30m," + Gain30m.ToString(nfi));
            lines.Add("Band Gain 20m," + Gain20m.ToString(nfi));
            lines.Add("Band Gain 17m," + Gain17m.ToString(nfi));
            lines.Add("Band Gain 15m," + Gain15m.ToString(nfi));
            lines.Add("Band Gain 12m," + Gain12m.ToString(nfi));
            lines.Add("Band Gain 10m," + Gain10m.ToString(nfi));
            lines.Add("Band Gain 6m," + Gain6m.ToString(nfi));

            lines.Add("Full Duplex," + Duplex.ToString(nfi));
            lines.Add("Only Tx on PTT," + OnlyTxOnPTT.ToString(nfi));

            lines.Add("Tune Level," + TuneLevel.ToString(nfi));
            lines.Add("CWPitch," + CWPitch.ToString(nfi));

            lines.Add("Penny OC Enable," + PennyOC.ToString(nfi));
            lines.Add("Penny OC 160mTx," + Penny160mTxOC.ToString(nfi));
            lines.Add("Penny OC 80mTx," + Penny80mTxOC.ToString(nfi));
            lines.Add("Penny OC 60mTx," + Penny60mTxOC.ToString(nfi));
            lines.Add("Penny OC 40mTx," + Penny40mTxOC.ToString(nfi));
            lines.Add("Penny OC 30mTx," + Penny30mTxOC.ToString(nfi));
            lines.Add("Penny OC 20mTx," + Penny20mTxOC.ToString(nfi));
            lines.Add("Penny OC 17mTx," + Penny17mTxOC.ToString(nfi));
            lines.Add("Penny OC 15mTx," + Penny15mTxOC.ToString(nfi));
            lines.Add("Penny OC 12mTx," + Penny12mTxOC.ToString(nfi));
            lines.Add("Penny OC 10mTx," + Penny10mTxOC.ToString(nfi));
            lines.Add("Penny OC 6mTx," + Penny6mTxOC.ToString(nfi));

            lines.Add("Penny OC 160mRx," + Penny160mRxOC.ToString(nfi));
            lines.Add("Penny OC 80mRx," + Penny80mRxOC.ToString(nfi));
            lines.Add("Penny OC 60mRx," + Penny60mRxOC.ToString(nfi));
            lines.Add("Penny OC 40mRx," + Penny40mRxOC.ToString(nfi));
            lines.Add("Penny OC 30mRx," + Penny30mRxOC.ToString(nfi));
            lines.Add("Penny OC 20mRx," + Penny12mRxOC.ToString(nfi));
            lines.Add("Penny OC 17mRx," + Penny17mRxOC.ToString(nfi));
            lines.Add("Penny OC 15mRx," + Penny15mRxOC.ToString(nfi));
            lines.Add("Penny OC 12mRx," + Penny12mRxOC.ToString(nfi));
            lines.Add("Penny OC 10mRx," + Penny10mRxOC.ToString(nfi));
            lines.Add("Penny OC 6mRx," + Penny6mRxOC.ToString(nfi));

            // write all the lines
            File.WriteAllLines(@"KK.csv", lines.ToArray());
        }

        public string LookupKKCSVValue(string key, string defaultValue, List<string> value)
        {
            for (int i = 0; i < value.Count; i += 2 )
            {
                string s = value[i];
                // search all the even index entries for a key that matches.  If there is one, return
                // the next list entry
                if (s.Equals(key, StringComparison.InvariantCulture))
                {
                    // they match.  return the next element in value
                    return value[i + 1];
                }
            }

            // not found.  Return default value instead
            return defaultValue;
        }

        public float LookupKKCSVValue(string key, float defaultValue, List<string> value)
        {
            for (int i = 0; i < value.Count; i += 2)
            {
                string s = value[i];
                // search all the even index entries for a key that matches.  If there is one, return
                // the next list entry
                if (s.Equals(key, StringComparison.InvariantCulture))
                {
                    // they match.  return the next element in value
                    try
                    {
                        return (float)Convert.ToDouble(value[i + 1], nfi);
                    }
                    catch
                    {
                        return defaultValue;
                    }
                }
            }

            // not found.  Return default value instead
            return defaultValue;
        }

        public int LookupKKCSVValue(string key, int defaultValue, List<string> value)
        {
            for (int i = 0; i < value.Count; i += 2)
            {
                string s = value[i];
                // search all the even index entries for a key that matches.  If there is one, return
                // the next list entry
                if (s.Equals(key, StringComparison.InvariantCulture))
                {
                    // they match.  return the next element in value
                    try
                    {
                        return Convert.ToInt32(value[i + 1], nfi);
                    }
                    catch
                    {
                        return defaultValue;
                    }
                }
            }

            // not found.  Return default value instead
            return defaultValue;
        }

        public bool LookupKKCSVValue(string key, bool defaultValue, List<string> value)
        {
            for (int i = 0; i < value.Count; i += 2)
            {
                string s = value[i];
                // search all the even index entries for a key that matches.  If there is one, return
                // the next list entry
                if (s.Equals(key, StringComparison.InvariantCulture))
                {
                    // they match.  return the next element in value
                    try
                    {
                        return Convert.ToBoolean(value[i + 1], nfi);
                    }
                    catch
                    {
                        return defaultValue;
                    }
                }
            }

            // not found.  Return default value instead
            return defaultValue;
        }

        public void ReadKKCSV()
        {
            // read the file into an array so we can process each entry
            var lines = File.ReadAllLines("KK.csv");
            List<string> value = new List<string>();

            // process the string array of data that was read
            foreach (string text in lines)
            {
                /* This gets around the problem of the locale of the user as far as saving floating point
                 * values which might (because of locale) have commas in the numbers, thus creating
                 * more entries in the array than desired, because of mis-parsing based on the extra commas.
                 * We also get to use a List<string> so that we don't need to know how big it is before we process
                 * data.  It indexes the same as 'string[] value' did.
                 */
                int p = text.IndexOf(",");      // search for the comma after the data element name
                if (p != -1)
                {
                    // we found a comma.  Split the line into 'before' and 'after' the comma
                    // before the comma is the data name
                    // after the comma is its value
                    value.Add(text.Substring(0, p));
                    value.Add(text.Substring(p + 1));
                }
            }

            // Assign the values in KK.csv to their respective variables
            //TODO: Save radio state on a per-band basis
            
            Your_call = LookupKKCSVValue("Users Call", Your_call, value);
            
           
            // is it by design, or an error, that BandSelect.Text isn't set to this value?
            BandText = LookupKKCSVValue("Band", BandText, value);
            SampleRate = LookupKKCSVValue("Sample Rate", SampleRate, value);
            stepSize.Text = LookupKKCSVValue("Step Size", stepSize.Text, value);
            Mode.Text = LookupKKCSVValue("Mode", Mode.Text, value);
            VolumeTrackBar.Value = LookupKKCSVValue("Volume", VolumeTrackBar.Value, value);
            AGCTrackBar.Value = LookupKKCSVValue("AGC-T", AGCTrackBar.Value, value);
            AGCSpeed.Text = LookupKKCSVValue("AGC Speed", AGCSpeed.Text, value);
            BandwidthTrackBar.Value = LookupKKCSVValue("Bandwidth", BandwidthTrackBar.Value, value);

            // use last set frequency when starting
            set_frequency_160 = LookupKKCSVValue("Last Freq 160m", set_frequency_160, value);
            set_frequency_80 = LookupKKCSVValue("Last Freq 80m", set_frequency_80, value);
            set_frequency_40 = LookupKKCSVValue("Last Freq 40m", set_frequency_40, value);
            set_frequency_30 = LookupKKCSVValue("Last Freq 30m", set_frequency_30, value);
            set_frequency_20 = LookupKKCSVValue("Last Freq 20m", set_frequency_20, value);
            set_frequency_17 = LookupKKCSVValue("Last Freq 17m", set_frequency_17, value);
            set_frequency_15 = LookupKKCSVValue("Last Freq 15m", set_frequency_15, value);
            set_frequency_12 = LookupKKCSVValue("Last Freq 120m", set_frequency_12, value);
            set_frequency_10 = LookupKKCSVValue("Last Freq 10m", set_frequency_10, value);
            set_frequency_6 = LookupKKCSVValue("Last Freq 6m", set_frequency_6, value);
            set_frequency.Value = LookupKKCSVValue("Last frequency", set_frequency.Value, value);
            
            ANF.Checked = LookupKKCSVValue("ANF", ANF.Checked, value);
            NR.Checked = LookupKKCSVValue("NR", NR.Checked, value);
            NB1.Checked = LookupKKCSVValue("NB1", NB1.Checked, value);
            NB2.Checked = LookupKKCSVValue("NB2", NB2.Checked, value);
            rcvr.BlockNBThreshold = LookupKKCSVValue("NB1 Threshold", rcvr.BlockNBThreshold, value);
            rcvr.AveNBThreshold = LookupKKCSVValue("NB2 Threshold", rcvr.AveNBThreshold, value);
            rcvr.PowerSpectrumAveragingOn = LookupKKCSVValue("Bandscope Average", rcvr.PowerSpectrumAveragingOn, value);
            rcvr.PowerSpectrumSmoothingFactor = LookupKKCSVValue("Bandscope Smooth", rcvr.PowerSpectrumSmoothingFactor, value);
            Rate = LookupKKCSVValue("Rate", Rate, value);

            // save the preamp setting for each band
            Preamp_160 = LookupKKCSVValue("Preamp 160", Preamp_160, value);
            Preamp_80 = LookupKKCSVValue("Preamp 80", Preamp_80, value);
            Preamp_40 = LookupKKCSVValue("Preamp 40", Preamp_40, value);
            Preamp_30 = LookupKKCSVValue("Preamp 30", Preamp_30, value);
            Preamp_20 = LookupKKCSVValue("Preamp 20", Preamp_20, value);
            Preamp_17 = LookupKKCSVValue("Preamp 17", Preamp_17, value);
            Preamp_15 = LookupKKCSVValue("Preamp 15", Preamp_15, value);
            Preamp_12 = LookupKKCSVValue("Preamp 12", Preamp_12, value);
            Preamp_10 = LookupKKCSVValue("Preamp 10", Preamp_10, value);
            Preamp_6 = LookupKKCSVValue("Preamp 6", Preamp_GC, value);
            Preamp_GC = LookupKKCSVValue("Preamp GC", Preamp_GC, value);

            ANFAdaptiveFilterSize = LookupKKCSVValue("ANFAdaptiveFilterSize", ANFAdaptiveFilterSize, value);
            ANFDelay = LookupKKCSVValue("ANFDelay", ANFDelay, value);
            ANFAdaptationRate = LookupKKCSVValue("ANFAdaptationRate", ANFAdaptationRate, value);
            ANFLeakage = LookupKKCSVValue("ANFLeakage", ANFLeakage, value);

            NRAdaptiveFilterSize = LookupKKCSVValue("NRAdaptiveFilterSize", NRAdaptiveFilterSize, value);
            NRDelay = LookupKKCSVValue("NRDelay", NRDelay, value);
            NRAdaptationRate = LookupKKCSVValue("NRAdaptationRate", NRAdaptationRate, value);
            NRLeakage = LookupKKCSVValue("NRLeakage", NRLeakage, value);

            GridMax = LookupKKCSVValue("Bandscope Grid Max", GridMax, value);
            GridMin = LookupKKCSVValue("Bandscope Grid Min", GridMin, value);
            GridStep = LookupKKCSVValue("Bandscope Grid Step", GridStep, value);


            AGCAttackTime = LookupKKCSVValue("AGC Attack Time", AGCAttackTime, value);
            AGCHangThreshold = LookupKKCSVValue("AGC Hang Threshold", AGCHangThreshold, value);
            AGCHangTime = LookupKKCSVValue("AGC Hang Time", AGCHangTime, value);
            AGCMaximumGainDB = LookupKKCSVValue("AGC Max Gain", AGCMaximumGainDB, value);
            AGCSlope = LookupKKCSVValue("AGC Slope", AGCSlope, value);
            AgcMethod = (SharpDSP2._1.AGCMethod)LookupKKCSVValue("AGC Method", (int)AgcMethod, value);

            WaterfallHighThreshold = LookupKKCSVValue("WaterfallHigh", WaterfallHighThreshold, value);
            WaterfallLowThreshold = LookupKKCSVValue("WaterfallLow", WaterfallLowThreshold, value);
            WaterfallAGC = LookupKKCSVValue("WaterFall AGC", WaterfallAGC, value);

            Squelch_level.Value = LookupKKCSVValue("Squelch level", Squelch_level.Value, value);
            PenneyPresent = LookupKKCSVValue("Penny Present", PenneyPresent, value);
            MicGain20dB = LookupKKCSVValue("Mic Gain 20dB", MicGain20dB, value);

            Atlas10MHz = LookupKKCSVValue("Atlas 10MHz", Atlas10MHz, value);
            Mercury10MHz = LookupKKCSVValue("Mercury 10MHz", Mercury10MHz, value);
            Penelope10MHz = LookupKKCSVValue("Penelope 10MHz", Penelope10MHz, value);
            Excalibur = LookupKKCSVValue("Excalibur Present", Excalibur, value);

            TxFilterHigh = LookupKKCSVValue("Tx Filter High", TxFilterHigh, value);
            TxFilterLow  = LookupKKCSVValue("Tx Filter Low", TxFilterLow, value);

            DriveLevel.Value = LookupKKCSVValue("Drive Level", DriveLevel.Value, value);

            Gain160m = LookupKKCSVValue("Band Gain 160m", Gain160m, value);
            Gain80m  = LookupKKCSVValue("Band Gain 80m", Gain80m, value);
            Gain60m  = LookupKKCSVValue("Band Gain 60m", Gain60m, value);
            Gain40m  = LookupKKCSVValue("Band Gain 40m", Gain40m, value);
            Gain30m  = LookupKKCSVValue("Band Gain 30m", Gain30m, value);
            Gain20m  = LookupKKCSVValue("Band Gain 20m", Gain20m, value);
            Gain17m  = LookupKKCSVValue("Band Gain 17m", Gain17m, value);
            Gain15m  = LookupKKCSVValue("Band Gain 15m", Gain15m, value);
            Gain12m  = LookupKKCSVValue("Band Gain 12m", Gain12m, value);
            Gain10m  = LookupKKCSVValue("Band Gain 10m", Gain10m, value);
            Gain6m   = LookupKKCSVValue("Band Gain 6m" , Gain6m, value);

            Duplex = LookupKKCSVValue("Full Duplex", Duplex, value);
            OnlyTxOnPTT = LookupKKCSVValue("Only Tx on PTT", OnlyTxOnPTT, value); 

            TuneLevel = LookupKKCSVValue("Tune Level", TuneLevel, value);
            
            CWPitch = LookupKKCSVValue("CWPitch", CWPitch, value);

            PennyOC = LookupKKCSVValue("Penny OC Enable",PennyOC, value);

            Penny160mTxOC = LookupKKCSVValue("Penny OC 160mTx", Penny160mTxOC, value);
            Penny80mTxOC = LookupKKCSVValue("Penny OC 80mTx", Penny80mTxOC, value);
            Penny60mTxOC = LookupKKCSVValue("Penny OC 60mTx", Penny60mTxOC, value);
            Penny40mTxOC = LookupKKCSVValue("Penny OC 40mTx", Penny40mTxOC, value);
            Penny30mTxOC = LookupKKCSVValue("Penny OC 30mTx", Penny30mTxOC, value);
            Penny20mTxOC = LookupKKCSVValue("Penny OC 20mTx", Penny20mTxOC, value);
            Penny17mTxOC = LookupKKCSVValue("Penny OC 17mTx", Penny17mTxOC, value);
            Penny15mTxOC = LookupKKCSVValue("Penny OC 15mTx", Penny15mTxOC, value);
            Penny12mTxOC = LookupKKCSVValue("Penny OC 12mTx", Penny12mTxOC, value);
            Penny10mTxOC = LookupKKCSVValue("Penny OC 10mTx", Penny10mTxOC, value);
            Penny6mTxOC = LookupKKCSVValue("Penny OC 6mTx", Penny6mTxOC, value);

            Penny160mRxOC = LookupKKCSVValue("Penny OC 160mRx", Penny160mRxOC, value);
            Penny80mRxOC = LookupKKCSVValue("Penny OC 80mRx", Penny80mRxOC, value);
            Penny60mRxOC = LookupKKCSVValue("Penny OC 60mRx", Penny60mRxOC, value);
            Penny40mRxOC = LookupKKCSVValue("Penny OC 40mRx", Penny40mRxOC, value);
            Penny30mRxOC = LookupKKCSVValue("Penny OC 30mRx", Penny30mRxOC, value);
            Penny20mRxOC = LookupKKCSVValue("Penny OC 20mRx", Penny20mRxOC, value);
            Penny17mRxOC = LookupKKCSVValue("Penny OC 17mRx", Penny17mRxOC, value);
            Penny15mRxOC = LookupKKCSVValue("Penny OC 15mRx", Penny15mRxOC, value);
            Penny12mRxOC = LookupKKCSVValue("Penny OC 12mRx", Penny12mRxOC, value);
            Penny10mRxOC = LookupKKCSVValue("Penny OC 10mRx", Penny10mRxOC, value);
            Penny6mRxOC = LookupKKCSVValue("Penny OC 6mRx", Penny6mRxOC, value);

        }


        public Form1()
        {
            //Control.CheckForIllegalCrossThreadCalls = false;  // leave on so we catch these
            InitializeComponent();
            listenerStart();                        //Start TCP Listener for SDT connection
            Debug.Indent();                         // Indent Debug messages to make them easier to see

            // draw 
            pictureBox2.Paint += new PaintEventHandler(pictureBox2_Paint); // SMeter event handler
            rcvr = new SharpDSP2._1.Receiver();
            SharpDSP2._1.PowerSpectrumSignal.ps_event += new SharpDSP2._1.PowerSpectrumSignal.PSpectrumEventHandler(PowerSpectrumSignal_ps_event);
            SharpDSP2._1.OutbandPowerSpectrumSignal.ps_event += new SharpDSP2._1.OutbandPowerSpectrumSignal.PSpectrumEventHandler(OutbandPowerSpectrumSignal_ps_event);
            state = rcvr.DSPStateObj;       // fetch state that Receiver constructor created
            state.DSPBlockSize = isize;     // put in some initial values

            // Set up for full bandwidth spectrum display
            FullBandwidthSpectrumState = new SharpDSP2._1.DSPState();
            FullBandwidthSpectrumState.DSPBlockSize = 4096;
            FullBandwidthSpectrumBuffer = new SharpDSP2._1.DSPBuffer(FullBandwidthSpectrumState);
            FullBandwidthSpectrum = new SharpDSP2._1.OutbandPowerSpectrumSignal(ref FullBandwidthSpectrumBuffer);
            VolumeTrackBar.Minimum = 0;
            VolumeTrackBar.Maximum = 100;
            AGCTrackBar.Minimum = 0;
            AGCTrackBar.Maximum = 100;

            // Load the previous radio settings from the KK.csv file and allocate values.
            // First check that the file exists
            if (!File.Exists("KK.csv"))   //if file doesn't exist, create it
            {
                CreateKKCSV();
            }

            // now read the lines in the config file.  Warn if action above didn't create one!
            // This allows KK.CSV to be no longer part of the SVN tree.  This means that the KK.CSV
            // file won't get mangled/updated by SVN when someone updates the code!
            if (File.Exists("KK.csv"))   //if file exists open it
            {
                ReadKKCSV();

                BandSelect.Text = BandText;    // set  band to start on 
                Frequency_change();                 // check we are in band, update frequency and display 
                ChangeMode();                       // Set Bandwidth slider value based on Mode in use
                state.DSPSampleRate = SampleRate;   // set the previously saved sample rate
                QuickMemory = set_frequency.Value;  // set a quick frequency value
                QuickMemoryBand = BandSelect.Text;  // set a quick frequency band

                //uu//
                

                TransmitFilterState = new SharpDSP2._1.DSPState();
                TransmitFilterState.DSPBlockSize = isize;
                TransmitFilterState.DSPSampleRate = SampleRate;
                TransmitFilterBuffer = new SharpDSP2._1.DSPBuffer(TransmitFilterState);
                TransmitFilter = new SharpDSP2._1.Filter(ref TransmitFilterBuffer);
                TransmitFilter.FilterType = SharpDSP2._1.FilterType_e.BandPass;
                TransmitFilter.FilterWindowType = SharpDSP2._1.WindowType_e.BLACKMANHARRIS_WINDOW;
                TransmitFilter.FilterFrequencyLow = TxFilterLow;
                TransmitFilter.FilterFrequencyHigh = TxFilterHigh;
                // Force the Form1 Controls to the values retrieved from KK.CSV
                SetVolume(this, EventArgs.Empty);           // force Volume control to accept previous saved value
                AGCTrackBar_Scroll(this, EventArgs.Empty);  // force AGC Threshold to accept previous saved value
                Filter_squelch_CheckedChanged(this, EventArgs.Empty); // force Squelch to accept previous saved value
                Squelch_setting.Text = Squelch_level.Value.ToString();
                DriveLevel_Scroll(this, EventArgs.Empty);   // force Drive contol to accept value from KK.csv
                

                // Force the Setup Form Controls to update
                SetupForm s = new SetupForm(this);              // get a reference to Setup form 
                s.IQScale_ValueChanged(this, EventArgs.Empty);  // set the IQ Scale value
                s.SampleRate.Text = SampleRate.ToString();      // set the current Sampling Rate
                s.SampleRate_SelectedIndexChanged(this, EventArgs.Empty);

                ModeText = Mode.Text;
                rcvr.LOFrequency = 0.0f;
                //rcvr.BinauralMode = true;             // currently has no effect
                rcvr.PowerSpectrumUpdateRate = 50;      // Set maximum PowerSpectrum update rate
                //rcvr.PowerSpectrumCorrection = 1f;
                rcvr.WindowType = SharpDSP2._1.WindowType_e.BLACKMAN3_WINDOW;
                // set up the squelch control 
                Squelch_level.Minimum = GridMin * 10; Squelch_level.Maximum = GridMax * 10;
                Squelch_setting.Text = (Squelch_level.Value / 10).ToString();

                previous_OnlyTxOnPTT = OnlyTxOnPTT;


                AudioRing = new SharpDSP2._1.RingBuffer(8192);          // create a ring buffer for rcvr audio output complex values
                TransmitAudioRing = new SharpDSP2._1.RingBuffer(8192);  // create a ring buffer for rcvr audio output complex values

                SignalBuffer = new SharpDSP2._1.DSPBuffer(state);

                // Calculate sample values for baseband CW note
                double deltaf = Math.PI / 40.0;     // (2 PI f / 48k) gives an 600 Hz note at 48 ksps
                for (int i = 0; i < 80; ++i)
                {
                    CWnoteSin[i] = (short)(29000.0 * Math.Sin(deltaf * i));
                    CWnoteCos[i] = (short)(29000.0 * Math.Cos(deltaf * i));
                }

                // calculate sample values for raised cosine profile for CW note
 
            }

            else  // TODO: can't find KK.csv file - should add some error handling or default values here.
            {
                MessageBox.Show("Can't find KK.csv, should be in the same directory as KISS Konsole.exe?", "File Error");
            }
           
            // Get Band from SDT and then set it
            
        }

        void PowerSpectrumSignal_ps_event(object source, SharpDSP2._1.PowerSpectrumSignal.PSpectrumEvent e)
        {
            Array.Copy(e.buffer, PowerSpectrumData, 2048);
        }

        void OutbandPowerSpectrumSignal_ps_event(object source, SharpDSP2._1.OutbandPowerSpectrumSignal.PSpectrumEvent e)
        {
            Array.Copy(e.buffer, FullBandwidthPowerSpectrumData, 8192);
        }

        private void ProcessWideBandData()
        {
            if (chkWideSpec.Checked) // display wide band spectrum
            {
                // Read new buffer of full-bandwidth
                int ret = libUSB_Interface.usb_bulk_read(hdev, 0x84, EP4buf, 8192, 100); //69ingChipmunks
                if (ret == 8192)
                {
                    byte[] temp = new byte[4];
                    float scaleIn = (float)(1.0 / Math.Pow(2, 15));
                    float Sample, RealAverage;
                    float[] SamplesReal = new float[4096];
                    float[] SamplesImag = new float[4096];

                    temp[2] = 0;
                    temp[3] = 0;
                    RealAverage = 0.0f;
                    int i, k;
                    for (i = 0, k = 0; i < 8192; i += 2, ++k)
                    {
                        // use this rather than BitConverter.ToInt16()...since its faster and less CPU intensive
                        // without the '(short)' in there, the value doesn't get sign-extended!
                        // so what should be small negative values show up as (almost) positive values?
                        Sample = scaleIn * (float)(short)((EP4buf[i] << 8) | (EP4buf[i + 1]));

                        RealAverage += Sample;
                        SamplesReal[k] = Sample;
                    }

                    RealAverage /= (float)k;
                    for (i = 0; i < k; ++i)
                    {
                        SamplesReal[i] -= RealAverage;      // Subtract average
                        SamplesImag[i] = SamplesReal[i];    // temporary -- soon will do digital down-conversion
                        // with sin & cos, so we'll actually have I & Q data to enter.
                    }

                    FullBandwidthSpectrum.Process(SamplesReal, SamplesImag, 4096);
                }
            }
        }

        
  
        public void UpdateFormTitle()
        {
           
            this.Text = "HPSDR  K.I.S.S. Konsole   " + version + "   " + Your_call;  // text on top of form 
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            UpdateFormTitle();

            this.StartPosition = FormStartPosition.CenterScreen;  // TODO save last postion and restore from KK.csv
            
            this.Width = 1040;
            this.Height = 800;
        }

       
        private void DrawSpectrumGrid(Graphics g)
        {
            #region How DrawGrid works
            /* 
             * We want to display a grid spaced 5/10/20kHz apart horizontally depending on the sampling rate.
             * We also want to display a vertical grid spaced zdB apart. We also want to indicate the 
             * current filter width on the display.
             * 
             * The screen is 1024 pixels wide so filter width =  bandwidth / sampling_rate
             * 
             * To display the grid, first calculate the vertical grid spacing. We want to display from +/-XdB to -YdBm in ZdB steps 
             * which means we need 160/20 = 8 horizontal lines.
             * 
             * We know the frequency at the left hand edge of the bandscope since it will be the tuned frequency less 
             * (sampling rate/2).  This will be displayed at the top of the first vertical grid line. We want the vertical 
             * grid to have spacings of 5/10/20kHz depending on the sampling rate. We also want the frequency markings to
             * be in these increments. So we need to find the first location from the left hand side of the screen that we
             * can divide by 5/10/20 and get no remainder.  We use the % function to do this. 
             * 
             * Since the minimium grid size is 5kHz we are going to have to test in finer increments than this such that
             * frequency/5 = 0 remainder.  So we use 100Hz increments - this gives a nice smooth display when we drag it.
             * 
             * Once we know the first frequency from the left hand edge of the bandscope we can convert this to pixels. 
             * We know the bandscope is 1024 pixels wide and the frequency width = sampling rate. So we can calculate the x 
             * location of the first vertical bar from  
             * 
             *  pixel_indent = Hz offset from LHS * width of bandscope/sample rate
             *  
             * We can now format the frequency text and display the lines. 
             * 
             * 
             */

            #endregion

            // get the height of our panel
            int BSHeight = pictureBoxSpectrum.Height;

            // calculate the number of horizontal lines and the distance between them
            double lines = (float)(GridMax - GridMin) / (float )GridStep;
            lines = Math.Ceiling((double)lines); // if lines is a fraction round up to next integer 
            int x_locate = 0;
            string displayFreq;
            int display_frequency = 0;
            int offset = 15; // how many pixels from the top to start drawing the vertical lines

            // determine vertical grid spacing
            int vertical_grid_spacing = (int)(BSHeight / lines);
            pixels_per_GridStep = vertical_grid_spacing;

            // determine horizontal grid spacing
            int grid_spacing = SampleRate / 960;  // gives 50/100/200 x 100Hz
            int pixel_spacing = pictureBoxSpectrum.Width * grid_spacing * 100 / SampleRate;  // how many pixels this spacing represents
            float pixels_per_100Hz = pictureBoxSpectrum.Width * 100.0f / SampleRate; // how many pixels per 100Hz across the screen 

            // Draw the background color
            g.FillRectangle(Brushes.DarkGreen, pictureBoxSpectrum.ClientRectangle);
            Font font = new Font(FontFamily.GenericMonospace, 12);

            if (chkSpec.Checked) // if the narrow banscope is selected display it
            {
                // Show the  filter bandwidth on the screen 
                // The screen is 1024 pixels wide so filter width =  bandwidth / sampling_rate
                FilterLeft = 512 + (int)(1024.0 * Math.Min(rcvr.FilterFrequencyLow, rcvr.FilterFrequencyHigh) / SampleRate);
                FilterRight = 512 + (int)(1024.0 * Math.Max(rcvr.FilterFrequencyLow, rcvr.FilterFrequencyHigh) / SampleRate);
                int Width = FilterRight - FilterLeft;
                // Draw the filter bandwidth in a different colour
                g.FillRectangle(Brushes.SeaGreen, FilterLeft, 0, Width, BSHeight);

                // truncate tuned frequency to 100Hz digits 
                int truncate_freq = set_frequency.Value / 100;

                // the frequency at the left edge of the screen will be the tuned frequency - sample rate/(2 x 100Hz)
                int left_edge_frequency = truncate_freq - SampleRate / 200;

                // we now step in from the left edge frequency until we have a frequency that divides by 
                // the grid spacing with no remainder. This will be the first frequency we display
                for (display_frequency = left_edge_frequency; display_frequency < truncate_freq - 12; display_frequency++)
                {
                    if ((display_frequency % grid_spacing) == 0)
                        break;
                }

                // how many pixels is this from the left edge of the screen
                float indent_pixels = (display_frequency - left_edge_frequency) * pixels_per_100Hz;

                // draw the vertical grid lines with their associated frequency
                for (float i = indent_pixels; i < pictureBoxSpectrum.Width; i += pixels_per_100Hz)
                {
                    if (display_frequency % grid_spacing == 0)  // are we on a grid boundry
                    {
                        // draw the vertical lines
                        g.DrawLine(Pens.DarkGray, i, offset, i, BSHeight); // offset stops the line going through the text

                        displayFreq = String.Format("{0:0.000}", (float)display_frequency / 10000);
                        if (display_frequency < 100000)  // so the decimal point in the frequency aligns with the grid line - perfectionist!
                            x_locate = (int)i - 17;
                        else
                            x_locate = (int)i - 26;
                        // draw the text above each line
                        g.DrawString(displayFreq, font, Brushes.White, x_locate, 0);
                    }
                    // highlight band edge in Red
                    if (display_frequency == set_frequency.Minimum / 100 || display_frequency == set_frequency.Maximum / 100)
                        g.DrawLine(Pens.Red, i, offset, i, BSHeight);

                    display_frequency++; // increment frequency in 100s of Hz 
                }

                // draw a single vertical line in the middle of the screen
                g.DrawLine(Pens.Yellow, pictureBoxSpectrum.Width / 2, offset, pictureBoxSpectrum.Width / 2, BSHeight);

                // Draw the horizontal lines and signal calibration 
                // First draw the horizontal lines so they don't cover the calibration text 
                // Only draw them for the narrow bandscope
                for (int k = 0; k < BSHeight; k += vertical_grid_spacing)
                    g.DrawLine(Pens.DarkGray, 0, k, pictureBoxSpectrum.Width, k);

                // The dBm value for the top of the screen is held in Grid.Max, we don't indicate this but we do the next line
                // This will be Grid.Max - Grid.Step
                int textSpacing = vertical_grid_spacing;
                for (int x = GridMax - GridStep; x > GridMin; x -= GridStep)
                {
                    string level = x.ToString() + "dBm";
                    g.DrawString(level, font, Brushes.White, 0, textSpacing - 9);
                    textSpacing += vertical_grid_spacing;
                }
            }

            // Draw horizontal lines  if we need to show I or Q and no bandscope is selected
            if (ShowI || ShowQ)
            {
                if (chkSpec.Checked)  // have lines alread so return
                    return;
                else
                {
                    for (int k = 0; k < pictureBoxSpectrum.Height; k += vertical_grid_spacing)
                        g.DrawLine(Pens.DarkGray, 0, k, pictureBoxSpectrum.Width, k);
                }
            }

            // draw cross if required, only used on narrow bandscope at the moment. 
            if (ShowCross)
            {
                g.DrawLine(Pens.Yellow, MousePositionX, 0, MousePositionX, BSHeight); // draw cross vertical line
                if (MousePositionY < BSHeight)  // with both displays check we are within the screen boundries
                    g.DrawLine(Pens.Yellow, 0, MousePositionY, pictureBoxSpectrum.Width, MousePositionY); // draw cross horizontal line
            }
       }

        private void DrawWidebandGrid(Graphics g)
        {
            #region How DrawGrid works
            /* 
             * We want to display a grid spaced 5/10/20kHz apart horizontally depending on the sampling rate.
             * We also want to display a vertical grid spaced 20dB apart. We also want to indicate the 
             * current filter width on the display.
             * 
             * The screen is 1024 pixels wide so filter width =  bandwidth / sampling_rate
             * 
             * To display the grid, first calculate the vertical grid spacing. We want to display from 0 to -160dBm in 20dB steps 
             * which means we need 160/20 = 8 horizontal lines.
             * 
             * We know the frequency at the left hand edge of the bandscope since it will be the tuned frequency less 
             * (sampling rate/2).  This will be displayed at the top of the first vertical grid line. We want the vertical 
             * grid to have spacings of 5/10/20kHz depending on the sampling rate. We also want the frequency markings to
             * be in these increments. So we need to find the first location from the left hand side of the screen that we
             * can divide by 5/10/20 and get no remainder.  We use the % function to do this. 
             * 
             * Since the minimium grid size is 5kHz we are going to have to test in finer increments than this such that
             * frequency/5 = 0 remainder.  So we use 100Hz increments - this gives a nice smooth display when we drag it.
             * 
             * Once we know the first frequency from the left hand edge of the bandscope we can convert this to pixels. 
             * We know the bandscope is 1024 pixels wide and the frequency width = sampling rate. So we can calculate the x 
             * location of the first vertical bar from  
             * 
             *  pixel_indent = Hz offset from LHS * width of bandscope/sample rate
             *  
             * We can now format the frequency text and display the lines. 
             * 
             * 
             */

            #endregion

            // get height from our pictureBox
            int WBHeight = pictureBoxWideband.Height;

            // calculate the number of horizontal lines and the distance between them
            double lines = (float)(GridMax - GridMin) / (float)GridStep;
            lines = Math.Ceiling((double)lines); // if lines is a fraction round up to next integer 
            int x_locate = 0;
            string displayFreq;
            // determine grid spacing
            int vertical_grid_spacing = (int)(WBHeight / lines);
            // determine horizontal grid spacing
            int grid_spacing = SampleRate / 960;  // gives 50/100/200 x 100Hz
            int pixel_spacing = pictureBoxWideband.Width * grid_spacing * 100 / SampleRate;  // how many pixels this spacing represents
            float pixels_per_100Hz = pictureBoxWideband.Width * 100.0f / SampleRate; // how many pixels per 100Hz across the screen 

            // Draw the background color
            Font font = new Font(FontFamily.GenericMonospace, 12);

            // if wide bandscope in use draw vertical lines for it plus frequency 
            if (chkWideSpec.Checked)
            {
                g.FillRectangle(Brushes.DarkGreen, pictureBoxWideband.ClientRectangle);

                int start = 0;

                float MHz = 0.0f;
                for (int i = 0; i < pictureBoxWideband.Width; i += 84)  // 84 gives 5MHz line spacing
                {
                    // draw the vertical lines
                    g.DrawLine(Pens.DarkGray, i, start + 15, i, 512); // start offset stops the line going through the text

                    displayFreq = String.Format("{0:0.0}", MHz);
                    if (MHz < 10)  // so the decimal point in the frequency aligns with the grid line 
                        x_locate = (int)i - 17;
                    else
                        x_locate = (int)i - 26;
                    // draw the text on top of each line
                    g.DrawString(displayFreq, font, Brushes.White, x_locate, start);
                    MHz += 5.0f;
                }

                // Draw the horizontal lines and signal calibration 
                // First draw the horizontal lines so they dont cover the calibration text 
                // Only draw them for the wide bandscope
                for (int k = start + vertical_grid_spacing; k < pictureBoxWideband.Height; k += vertical_grid_spacing)
                    g.DrawLine(Pens.DarkGray, 0, k, pictureBoxWideband.Width, k);

                // The dBm value for the top of the screen is held in Grid.Max, we don't indicate this but we do the next line
                // This will be Grid.Max - Grid.Step
                int textSpacing = vertical_grid_spacing;
                for (int x = GridMax - GridStep; x > GridMin; x -= GridStep)
                {
                    string level = x.ToString() + "dBm";
                    g.DrawString(level, font, Brushes.White, 0, start + textSpacing - 9);
                    textSpacing += vertical_grid_spacing;
                }
            }
        }

        private void GetIQSamples()
        {
            float scaleIQDisplay = 20 * (float)Math.Pow(2, 15); // scale factor to convert DSP output to 16-bit short int audio output sample

            // read I & Q values from the buffer and plot the sample on the scope
            for (int i = 0; i < 1024; i++)
            {
                I_sample[i] = (int)(scaleIQDisplay * SignalBuffer.cpx[i].real);
                Q_sample[i] = (int)(scaleIQDisplay * SignalBuffer.cpx[i].imag);
            }
        }

        int waterfall_low_threshold = -120;
        public int WaterfallLowThreshold
        {
            get { return waterfall_low_threshold; }
            set { waterfall_low_threshold = value; }
        }

        int waterfall_high_threshold = -40;
        public int WaterfallHighThreshold
        {
            get { return waterfall_high_threshold; }
            set { waterfall_high_threshold = value; }
        }

        Point[] IDraw;
        Point[] QDraw;
        Point[] bandDraw;
        Point[] wideBandDraw;

        // computed during one ComputeDisplay block, and used in the next.
        // May be set to WaterfallLowThreshold if not doing waterfall AGC.
        // This value, and not waterfall_low_threshold, is used in the calculations in ComputeDisplay
        int waterfallPreviousMinValue = 0;

        private void ComputeDisplay()
        {
            int xmax = pictureBoxSpectrum.Width; // max number of samples across the screen

            int BSHeight = pictureBoxSpectrum.Height;
            int WBHeight = pictureBoxWideband.Height;
            double lines = (float)(GridMax - GridMin) / (float)GridStep;
            lines = Math.Ceiling((double)lines); // if lines is a fraction round up to next integer 

            int ymax = BSHeight; // maximum y value

            int yscale = IQScale;  // held on Setup form, sets X axis gain when displaying I and Q signals

            float PreampOffset = (PreampOn) ? -20.0f : 0.0f;  // if the preamp is on then remove 20dB from the signal level

            int xWidth = Math.Min(xmax, pictureBoxSpectrum.Width);
            int waterfall_minimum = 0;  // reset waterfall_minimum value in nPS array 
            max_signal = -200;          // reset maximum value of nPS in the array

            IDraw = new Point[xWidth];
            QDraw = new Point[xWidth];
            bandDraw = new Point[xWidth];
            wideBandDraw = new Point[xWidth];
            //int xpos = 0;

            // advance to the next waterfall line and count it
            if (chkWaterFall.Checked)
            {
                NextWaterfallLine();

                if (!WaterfallAGC)  // if selected then set the low threshold automatically
                    waterfallPreviousMinValue = WaterfallLowThreshold;
            }

            for (int xpos = 0; xpos < xWidth; xpos++)
            {
                float Sample;
                float SampleMax = 0.0f;
                int nI = I_sample[xpos];
                int nQ = Q_sample[xpos];

                #region How scaling the bandscope works
                /*
                 * We need to scale the bandscope display depending on the max,min and step values
                 * the uses has selected on the Setup Form.
                 * We know the number of pixels that each grid line represents since this is 
                 * calculated in DrawGrid() as vertical_grid_spacing.
                 * 
                 * Hence we can calculate the number of pixles/dB of vertical resolution from
                 * 
                 *  pixels_per_dB = vertical_grid_spacing/GridStep.
                 *  
                 * We know the signal level at the top of the bandscope since this is held in GridMax.
                 * 
                 * We need to know what each sample represents in terms of dBs.
                 * To do this we feed a known signal into Mercury with the preamp off. 
                 * 
                 * With this signal as input we  determine the maximum signal in the array and note the value.
                 * The signal processing to this point has been:
                 * 
                 *       Po =  (FFT(Pin) * G)  
                 * 
                 * where G = the overall gain from the antenna socket to here
                 *     Pin = the power level at the antenns socket in dBm and 
                 *     Po  = the  level we wish  to display on the bandscope
                 *     FFT = the fast fourier transform of the input signal 
                 *     
                 * We want the banscope to the scalled in dBm so we take log and multiply by 10 i.e.
                 * 
                 *     10 log Po = 10log (FFT(Pin) * G)
                 *  
                 *  or 10 log Po = 10log (FFT(Pin) + 10Log G 
                 * 
                 * Since the relations between signal input and (FFT(Pin) is linear 
                 * if we know Pin and measure Po we can calculate 10log G.
                 * 
                 * For example, with my Mercury board, 0dBm gives a peak value of  85 (This value will be calibrated in future releases)
                 *
                 *  hence  85 = 0 + 10log G 
                 * 
                 * We can now calculate the dB value of every sample.  We need to map these to the screen.
                 * 
                 * For example, if we assume the top of the screen is 0dBm then we can calculate each samples
                 * pixel position as follows 
                 * 
                 *  yintPS = -(nPS - 85)* pixels_per_db  or -(nPS  - 85) * vertical_grid_spacing/GridStep
                 *  
                 * For values of GridMax other than 0dBm we need need to subtract this value, hence
                 * 
                 *  yintPS = -(nPS - 85 - GridMax) * vertical_grid_spacing / GridStep;
                 *  
                 * We do exactly the same calculations for the wide bandscope - in this case 10log G = 56
                 * 
                 */
                #endregion

                int logGain = 85;  // see above as to why this value TODO: allow user to calibrate this value

                // TODO: we have 2048 samples in the FFT we could average these to 1024 pixels perhaps 
                int nPS = (int)(PowerSpectrumData[2 * xpos] + PreampOffset);

                // Display FullBandwidthPowerSpectrumData on the same grid.
                // FullBandwidthPowerSpectrumData are a block of 4096 contiguous undecimated samples taken at 122.88 Msps.
                // PowerSpectrumData comes from the decimated samples.
                // Decimation results in processing gain, so, while the two spectra
                // have the same scale, when plotting, their offsets differ.
                // Since we have more data points than horizontal pixels in our graph,
                // search for & display the largest signal in each group of four.

                SampleMax = -999.0f;
                for (int k = 0; k < 4; ++k)
                {
                    // we have 2 x 4095 samples since we use a real signal so start half way 
                    Sample = FullBandwidthPowerSpectrumData[4095 + 4 * xpos + k];
                    if (Sample > SampleMax) SampleMax = Sample;
                }
                int nFBPS = (int)(SampleMax + PreampOffset);

                int yintI = (int)ymax / 2 + (int)(nI >> yscale);
                int yintQ = (int)ymax / 2 + (int)(nQ >> yscale);

                int yintPS = -(nPS - logGain - GridMax) * (int)(BSHeight / lines) / GridStep;  // see explanation above

                // if the both bandscopes are in use with need to move this one down the screen by BSHeight
                int offset = 0;
                int yintFBPS = offset - (nFBPS - 56 - GridMax) * (int)(WBHeight / lines) / GridStep; // see explanation above

                // Draw the sample point by adding a point to the respective point lists
                // when we've exited the for loop, use DrawCurve for each that we are drawing
                if (ShowI) IDraw[xpos] = new Point(xpos, yintI); // g.DrawLine(YellowPen, xlast, ylastI, xpos, yintI);
                if (ShowQ) QDraw[xpos] = new Point(xpos, yintQ); // g.DrawLine(RedPen, xlast, ylastQ, xpos, yintQ);
                if (chkSpec.Checked) bandDraw[xpos] = new Point(xpos, yintPS); // g.DrawLine(WhitePen, xlast, ylastPS, xpos, yintPS);
                if (chkWideSpec.Checked) wideBandDraw[xpos] = new Point(xpos, yintFBPS); // g.DrawLine(ChartreusePen, xlast, ylastFBPS, xpos, yintFBPS);
                
                // this should be moved to one-time processing.  It's not needed to be done for each block of data being displayed.
                if (chkWaterFall.Checked)
                {              
                    // This code is based on the waterfall in PowerSDR 

                    // set to just above black for now for testing but make a user setting and save in KK.csv
                    int waterfall_low_color_R = 1; int waterfall_low_color_G = 1; int waterfall_low_color_B = 1;

                    int R = 0, G = 0 , B = 0;

                    // from the above explanation we know the dBm value of each sample is  nPS - logGain
                    int waterfall_data = nPS - logGain;
                    // select a colour for each pixel depending on its dBm value
                    if (waterfall_data <= waterfallPreviousMinValue)
                    {
                        R = waterfall_low_color_R;
                        G = waterfall_low_color_G;
                        B = waterfall_low_color_B;
                    }
                    else if (waterfall_data >= waterfall_high_threshold)
                    {
                        R = 192;  // looks like a mauve/purple sort of colour 
                        G = 124;
                        B = 255;
                    }
                    else // value is between low and high  
                    {
                        float range = waterfall_high_threshold - waterfallPreviousMinValue;
                        float off_set = waterfall_data - waterfallPreviousMinValue;
                        float overall_percent = off_set / range; // value from 0.0 to 1.0 where 1.0 is high and 0.0 is low.

                        if (overall_percent < (float)2 / 9) // background to blue
                        {
                            float local_percent = overall_percent / ((float)2 / 9);
                            R = (int)((1.0 - local_percent) * waterfall_low_color_R);
                            G = (int)((1.0 - local_percent) * waterfall_low_color_G);
                            B = (int)(waterfall_low_color_B + local_percent * (255 - waterfall_low_color_B));
                        }
                        else if (overall_percent < (float)3 / 9) // blue to blue-green
                        {
                            float local_percent = (overall_percent - (float)2 / 9) / ((float)1 / 9);
                            R = 0;
                            G = (int)(local_percent * 255);
                            B = 255;
                        }
                        else if (overall_percent < (float)4 / 9) // blue-green to green
                        {
                            float local_percent = (overall_percent - (float)3 / 9) / ((float)1 / 9);
                            R = 0;
                            G = 255;
                            B = (int)((1.0 - local_percent) * 255);
                        }
                        else if (overall_percent < (float)5 / 9) // green to red-green
                        {
                            float local_percent = (overall_percent - (float)4 / 9) / ((float)1 / 9);
                            R = (int)(local_percent * 255);
                            G = 255;
                            B = 0;
                        }
                        else if (overall_percent < (float)7 / 9) // red-green to red
                        {
                            float local_percent = (overall_percent - (float)5 / 9) / ((float)2 / 9);
                            R = 255;
                            G = (int)((1.0 - local_percent) * 255);
                            B = 0;
                        }
                        else if (overall_percent < (float)8 / 9) // red to red-blue
                        {
                            float local_percent = (overall_percent - (float)7 / 9) / ((float)1 / 9);
                            R = 255;
                            G = 0;
                            B = (int)(local_percent * 255);
                        }
                        else // red-blue to purple end
                        {
                            float local_percent = (overall_percent - (float)8 / 9) / ((float)1 / 9);
                            R = (int)((0.75 + 0.25 * (1.0 - local_percent)) * 255);
                            G = (int)(local_percent * 255 * 0.5);
                            B = 255;
                        }
                    }
                    // Find the lowest signal level in the block of 1024 samples. If the automatic check box
                    // is selected then use this value for waterfall_low_threshold next time through.
                    if (waterfall_minimum > waterfall_data)
                         waterfall_minimum = waterfall_data;

                    waterfallBitmap.SetPixel(xpos, currentWaterfallLine, Color.FromArgb(R,G,B));
                }
                // Find the maximum signal level in the block of 1024 samples for use in the wideband squelch
                if (Bandscope_squelch.Checked &&  xpos > 0 && max_signal < (nPS - logGain)) //TODO: sample 0 is always 85 - why?
                    max_signal = (nPS - logGain);

            } // end for each x data point

            // Now store the waterfall_minumum value, so that it is available for the next packet to use
            // Provide  some 'history' (smoothing) in the WaterfallAGC i.e. 80% of the previous value and 20% of the current
            waterfallPreviousMinValue = ((waterfallPreviousMinValue * 8) + (waterfall_minimum * 2)) / 10;
        }

        private int currentWaterfallLine = 0;
        private int numWaterfallLines = 0;

        private void ClearWaterfall()
        {
            currentWaterfallLine = 0;
            numWaterfallLines = 0;
        }

        private void NextWaterfallLine()
        {
            if (--currentWaterfallLine < 0)
            {
                currentWaterfallLine = waterfallBitmap.Height - 1;
            }

            if (numWaterfallLines < waterfallBitmap.Height)
            {
                ++numWaterfallLines;
            }

            if (numWaterfallLines == 1)
            {
                Color backColor = Color.Black;

                // about to make first use!  set all to black!
                for (int r = 0; r < waterfallBitmap.Height; ++r)
                {
                    for (int c = 0; c < waterfallBitmap.Width; ++c)
                    {
                        waterfallBitmap.SetPixel(c, r, backColor);
                    }
                }
            }
        }

        private Bitmap waterfallBitmap = new Bitmap(1024, 256, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        private void RefreshScope(Graphics g)
        {
            if (ShowI && (IDraw != null)) g.DrawCurve(YellowPen, IDraw);
            if (ShowQ && (QDraw != null)) g.DrawCurve(RedPen, QDraw);
            if (chkSpec.Checked && (bandDraw != null)) g.DrawCurve(WhitePen, bandDraw);
            if (chkWideSpec.Checked && (wideBandDraw != null)) g.DrawCurve(ChartreusePen, wideBandDraw);

            if (chkWaterFall.Checked)
            {
                int height = 256;  //use BSHeight when working 
                // we have a partial bitmap only.  display it in one piece
                if (waterfallBitmap != null)
                {
                    int firstLine = currentWaterfallLine;
                    int firstSegmentLength = waterfallBitmap.Height - currentWaterfallLine;
                    Rectangle destRect = new Rectangle(0, height, 1024, firstSegmentLength);

                    // Create rectangle for source image.  This is the first part, from currentWaterfallLine to the end of the bitmap
                    Rectangle srcRect = new Rectangle(0, currentWaterfallLine, 1024, firstSegmentLength);
                    GraphicsUnit units = GraphicsUnit.Pixel;

                    // Draw image to screen, part 1.
                    g.DrawImage(waterfallBitmap, destRect, srcRect, units);

                    // draw rest of the image (which may be blank/black) which is the FIRST PART of the bitmap, to AFTER where
                    // the first part was drawn
                    destRect.Y = height + firstSegmentLength;
                    destRect.Height = currentWaterfallLine;
                    srcRect.Y = 0;
                    srcRect.Height = currentWaterfallLine;
                    g.DrawImage(waterfallBitmap, destRect, srcRect, units);
                }
            }       
        }


        private void RefreshSpectrumScope(Graphics g)
        {
            if (ShowI && (IDraw != null)) g.DrawCurve(YellowPen, IDraw);
            if (ShowQ && (QDraw != null)) g.DrawCurve(RedPen, QDraw);
            if (chkSpec.Checked && (bandDraw != null)) g.DrawCurve(WhitePen, bandDraw);

            // set the range of the squelch contol to the max and min bandscope values x10 to give high resolution
            Squelch_level.Minimum = GridMin * 10; Squelch_level.Maximum = GridMax * 10;

            if (Filter_squelch.Checked || Bandscope_squelch.Checked)
            {
                squelch_y_value = (int)(-((Squelch_level.Value / 10) - (GridMax)) * pixels_per_GridStep / GridStep);
                // draw dotted line to represent squelch threshold
                for (int x = 0; x < pictureBoxSpectrum.Width - 5; x += 10)
                    g.DrawLine(Pens.DarkOrange, x, squelch_y_value, x + 5, squelch_y_value);

                // draw the little rectangle at the left side of the squelch threshold line
                squelch.Height = 10; squelch.Width = 10; squelch.X = 0;
                squelch.Y = squelch_y_value - squelch.Height / 2; // make it a square
                g.FillRectangle(Brushes.DarkOrange, squelch);   // fill the rectangle with colour
            }
        }

        private void RefreshWidebandScope(Graphics g)
        {
            if (chkWideSpec.Checked && (wideBandDraw != null)) g.DrawCurve(ChartreusePen, wideBandDraw);

            if (chkWaterFall.Checked)
            {
                int height = pictureBoxWideband.Height;
 
                // display the waterfall bitmap, if we have one
                // display it in two parts: from the currentWaterfallLine to the end of the array,
                // 
                if (waterfallBitmap != null)
                {
                    int firstLine = currentWaterfallLine;
                    int firstSegmentLength = waterfallBitmap.Height - currentWaterfallLine;
                    Rectangle destRect = new Rectangle(0, 0, 1024, firstSegmentLength);

                    // Create rectangle for source image.  This is the first part, from currentWaterfallLine to the end of the bitmap
                    Rectangle srcRect = new Rectangle(0, currentWaterfallLine, 1024, firstSegmentLength);
                    GraphicsUnit units = GraphicsUnit.Pixel;

                    // Draw image to screen, part 1.
                    g.DrawImage(waterfallBitmap, destRect, srcRect, units);

                    // draw rest of the image (which may be blank/black) which is the FIRST PART of the bitmap, to AFTER where
                    // the first part was drawn
                    destRect.Y = firstSegmentLength;
                    destRect.Height = currentWaterfallLine;
                    srcRect.Y = 0;
                    srcRect.Height = currentWaterfallLine;
                    g.DrawImage(waterfallBitmap, destRect, srcRect, units);
                }
            }
        }

        Pen YellowPen = Pens.Yellow;
        Pen RedPen = Pens.Red;
        Pen WhitePen = Pens.White;
        Pen ChartreusePen = Pens.Chartreuse;

        private bool start_USB()
        {
            // look for USB connection to Ozy
            hdev = USB.InitFindAndOpenDevice(0xfffe, 0x0007);
            if (hdev.ToInt32() == 0)
                return false;

            int ret;
            ret = libUSB_Interface.usb_set_configuration(hdev, 1);
            ret = libUSB_Interface.usb_claim_interface(hdev, 0);
            ret = libUSB_Interface.usb_set_altinterface(hdev, 0);
            ret = libUSB_Interface.usb_clear_halt(hdev, 0x02);
            ret = libUSB_Interface.usb_clear_halt(hdev, 0x86);
            ret = libUSB_Interface.usb_clear_halt(hdev, 0x84);
            return true;
        }

        // stop USB interface when we exit the program
        private void stop_USB()
        {
            if (hdev.ToInt32() != 0) // check we have an open USB port 
            {
                // K9TRV gets an error here on the usb_close for hdev being invalid, most likely as a result of the
                // call to usb_release_interface...
                // looks like the usb_close should close the interface...
                // the error/exception can happen under many circumstances for me.
                // Best to place in try/catch so the exceptions are caught, but not reported to the user
                try
                {
                    libUSB_Interface.usb_release_interface(hdev, 0);
                    libUSB_Interface.usb_close(hdev);
                }
                catch
                {
                }
            }
        }

        // This thread runs at program load and reads data from Ozy
        public void USBLoop()
        {
            int ret;
            for ( ; ; ) // do this forever 
            {
                if (KK_on)  // only read ADC data if USB is selected
                {
                    ret = libUSB_Interface.usb_bulk_read(hdev, 0x86, rbuf, 2048, 1000); // rbuf.Length = 2048 //69ingChipmunks
                     if (ret == 2048)
                        Process_Data(ref rbuf);
                    else
                    {
                        // we can't read from EP6 for some reason - perhaps Ozy not primed so 
                        // send some C&C frames to prime the pump.
                        bool force = true;  
                        Thread.Sleep(10); // so we don't end up in a tight loop 
                        Debug.WriteLine("EP6 Read Error -  returns \t" + ret);
                        Debug.WriteLine("EP6 Read Error -  hdev \t" + hdev);
                        Mercury_send(force);  // force C&C data to Ozy to get started 
                    }
                }
                else Thread.Sleep(100);// Nothing selected so sleep for 100mS so we don't hog all the CPU
            } // for ever 
        } // USB_loop 

        // this is called when we exit the program - terminate the USB connection and save the 
        // current state of the Radio in the KK.csv file.
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (USB_thread_running)
                USB_thread.Abort();  // stop USB thread

            if (hdev.ToInt32() != 0)  // check we have an active USB port
                stop_USB();

            // save the last frequency used 
            switch (BandSelect.Text)
            {
                case "160m": set_frequency_160 = set_frequency.Value; break;
                case "80m": set_frequency_80 = set_frequency.Value; break;
                case "40m": set_frequency_40 = set_frequency.Value; break;
                case "30m": set_frequency_30 = set_frequency.Value; break;
                case "20m": set_frequency_20 = set_frequency.Value; break;
                case "17m": set_frequency_17 = set_frequency.Value; break;
                case "15m": set_frequency_15 = set_frequency.Value; break;
                case "12m": set_frequency_12 = set_frequency.Value; break;
                case "10m": set_frequency_10 = set_frequency.Value; break;
                case "6m": set_frequency_6 = set_frequency.Value; break;
                case "GC": set_frequency_GC = set_frequency.Value; break;
            }
            // save program settings in KK.csv file
            WriteKKCSV();
        }

        private void SetSMeterGraph(double value)
        {
            // Calibrate SMeter. S9 = -73dBm = 70 on scale hence add 140
            int sValue = (int)value + 140; 
            if (sValue > 140)       // check the SMeter value is within the range we can display so we don't damage it
                sValue = 140;
            else if (sValue < 0)
                sValue = 0;
            else
            {
                SMeterValue = (int)sValue;
                pictureBox2.Invalidate();
            }
        }

        private string SMeterText(double avgValue, double instantaneousValue)
        {
            if (PreampOn)  // Compensate for 20dB of Preamp gain
            {
                avgValue -= 20;
                instantaneousValue -= 20;
            }

            // Calibrate readings. With 1kHz bandwidth noise floor is -115dBm/-135dBm  preamp on/off
            // Raw values are -45 with preamp off so need to add -70
            avgValue += -70.0f;
            instantaneousValue += -70.0f;
            // set the max signal level for use by the Squelch control
            filter_max_signal = (int)avgValue; 

            // set the bargraph (meter)
            SetSMeterGraph(instantaneousValue);

            // could also compensate with a 'global correction' factor
            return String.Format(nfi, "SMeter: Avg {0:F1} : Inst {1:F1}", avgValue, instantaneousValue);
        }

        private string SMeterText(SharpDSP2._1.SignalMeter s)
        {
            return SMeterText(s.AvgValue, s.InstValue);
        }

        private void SetSMeterText(string text)
        {
            labelSMeter.Text = text;
        }

        // This delegate enables asynchronous calls for setting
        // the text property on a TextBox control.
        delegate void SetTextCallback(string text);

        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.labelSMeter.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                SetSMeterText( text);
            }
        }

        int sMeterCount = 0;
        private void SMeter(Object o, SharpDSP2._1.SignalMeter.SignalMeterEvent s)
        {
            if (++sMeterCount == 10)
            {
                sMeterCount = 0;
                SetText(SMeterText(s.Average, s.Instantaneous));
            }
        }

        // Draw the SMeter and display the signal level
        void pictureBox2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            // We only need to draw the SMeter backrough once, then use it as an image 
            // saves CPU cycles

            if (SMeterBackgroundImage == null)
            {
                pictureBox2.Width = 140;
                pictureBox2.Height = 40;
                Meter = pictureBox2.ClientRectangle;
                float MeterHeigth = pictureBox2.Height - 3;
                float MeterWidth = pictureBox2.Width - 5;
                pictureBox2.BackColor = Color.Black;
                Brush whiteBrush = Brushes.White;
                Brush redBrush = Brushes.Red;
                Pen whitePen = new Pen(Color.White, 2);
                Pen narrowWhitePen = new Pen(Color.White, 1);
                Pen redPen = new Pen(Color.Red, 2);
                Pen narrowRedPen = new Pen(Color.Red, 1);
                float MarkerSpacing = 14; // Meter.Width / 10.0f;
                string marker = null;

                // Draw the white items
                for (float x = 5, y = 12, z = 1; x < Meter.Width / 2; x += MarkerSpacing, y += MarkerSpacing, z += 2)
                {
                    marker = z.ToString();
                    g.DrawLine(narrowWhitePen, x, MeterHeigth, x, 0.9f * MeterHeigth); //small ticks
                    g.DrawLine(whitePen, y, MeterHeigth, y, 0.8f * MeterHeigth); //big ticks
                    g.DrawString(marker, meterFont, whiteBrush, y - 5, 0.45f * MeterHeigth);
                }

                // Draw the red items
                MarkerSpacing = 22; // Meter.Width / 6.5f;  // wider spacing for these itesm 

                for (float x = 67 + MarkerSpacing / 2, y = 67 + MarkerSpacing, z = 0; x < Meter.Width; x += MarkerSpacing, y += MarkerSpacing, z++)
                {
                    if (z == 0) marker = "+20";
                    else if (z == 1) marker = "+40";
                    else if (z == 2) marker = "+60";
                    g.DrawLine(narrowRedPen, x, MeterHeigth, x, 0.9f * MeterHeigth); //small ticks
                    g.DrawLine(redPen, y, MeterHeigth, y, 0.8f * MeterHeigth); //big ticks
                    g.DrawString(marker, meterFont, redBrush, y - 10, 0.45f * MeterHeigth);
                }

                // Draw horizontal lines to finsh off
                g.DrawLine(whitePen, 0, MeterHeigth, MeterWidth / 2, MeterHeigth);
                g.DrawLine(redPen, 1 + MeterWidth / 2, MeterHeigth, MeterWidth - 1, MeterHeigth);

                SMeterBackgroundImage = pictureBox2.Image;
                pictureBox2.BackgroundImage = SMeterBackgroundImage;
            }
            else
            {
            }

            // now draw SMeter value as a vertical bar
            // scale below S9 is 7 pixels per marker and 11 above so need to scale accordingly
            Pen yellowPen = new Pen(Color.Yellow, 2);
            SMeterValue = (int)((float)SMeterValue * 1.15f) - 10; 
            if (SMeterValue > 68)  // scale changes at the S9 level
                SMeterValue = (int)((float) SMeterValue * 0.98f);
            g.DrawLine(yellowPen, SMeterValue, 0, SMeterValue, pictureBox2.Height);
        }

        float MicGain = (float) (1.0 / Math.Pow(2,15)) ;
        double MicAGC = 1.0;

        // get data from Ozy via the USB port
        public void Process_Data(ref byte[] rbuf)
        {
            #region how Process_Data works

            // Process_Data  capitalizes on the fact that the USB data from Ozy is well ordered and
            // consistent as it arrives from the USB bulk read with respect to where sync pulses and rCn, I, Q,
            // and mic values are located in the array. A simple initial check for the presence of sync pulses
            // at the beginning of rbuf is all that is done to establish that the data within rbuf is likely
            // to be valid Rx data

            // in the 2048 bytes of rbuf there are 4 frames of 63 samples yielding a total 
            // of 504 samples.  A sample consists of I (3bytes), Q (3 bytes) and mic (2 bytes).
            // Each frame contains 512 bytes with 3 sync bytes & 5 rCn bytes as the leading bytes of 
            // the frame...JAM

            // The first bytes of a frame consist of 3 sync bytes + rC0 rC1 rC2 rC3 rC4 (5 bytes)
            // + I sample (3bytes) + Q sample (3bytes) + mic sample (2bytes)= 16 bytes for the first few 
            // bytes in rbuf...JAM

            // rc0 bits 7-2 is command and control "address"
            // rc0 bit 1 is "dash"
            // rc0 bit 0 is "dot" or PTT from penelope

            // when rc0 "address" == 0
            // rc1 bit 0 is Mercury ADC overload bit
            // rc2 is Mercury software serial number
            // rc3 is Penelope software serial number
            // rc4 is Ozy software serial number

            // when rc0 is binary 000001xx
            // rc1 is bits 15-8 of penelope forward power (only 12 bits used)
            // rc2 is bits 7-0 of penelope forward power

            // For a full description of the USB protocol see the document in \trunk\Documents

            #endregion

            byte[] temp = new byte[4];
            float scaleIn = (float)(1.0 / Math.Pow(2, 23));     // scale factor for converting 24-bit int from ADC to float

            // check that sync pulses are present in the front of rbuf...JAM
            if (rbuf[0] == sync && rbuf[1] == sync && rbuf[2] == sync)
                IsSynced = true;  // use this to set the colour of the Sync LED when the timer fires
            else
            {
                IsSynced = false;
                Debug.WriteLine("Sync Failed - rbuf = \t" + rbuf);
                return;
            }

            for (int frame = 0; frame < 4; frame++)
            {
                int coarse_pointer = frame * 512; //512 bytes total in each frame
                rc0 = rbuf[coarse_pointer + 3];
                rc1 = rbuf[coarse_pointer + 4];
                rc2 = rbuf[coarse_pointer + 5];
                rc3 = rbuf[coarse_pointer + 6];
                rc4 = rbuf[coarse_pointer + 7];

                if ((rc0 & 0x01) != 0)
                {
                    PTT = true;  // PTT active on Atlas bus so set PTT flag
                }
                else
                {
                    PTT = false;
                }
                if ((rc0 & 0xfc) == 0)
                {
                    if ((rc1 & 1) == 0) ADCOverload = false; // check for ADC overload 
                    else ADCOverload = true;
                }

                // get serial # of Mercury and Penelope software
                // Only need to do this once when KK starts
                if ((rc0 & 0xfc) == 0)
                {
                    if (loop_count == 99)  // wait for 100 reads so Ozy data is stable
                    {
                        Merc_version = (int)rc2;        // Version number of Mercury FPGA code
                        Penny_version = (int)rc3;       // Version number of Penelope FPGA code
                        Ozy_FPGA_version = (int)rc4;    // Version number of Ozy FPGA code

                        Debug.WriteLine("Mercury version = \t" + Merc_version);
                        Debug.WriteLine("Penny version = \t" + Penny_version);
                        Debug.WriteLine("Ozy version = \t" + Ozy_FPGA_version);

                        if (Merc_version < 27)
                            MessageBox.Show(" Warning - Mercury code version is V" + ((float)(Merc_version / 10f)).ToString() +
                            " \n Must be V2.7 or higher");

                        // TODO: display FPGA code versions on Setup form  *** Note - cross thread  *** 
                        //label1.Text = Merc_version.ToString();      // Version number of Mercury FPGA code
                        //label6.Text = Penny_version.ToString();     // Version number of Penelope FPGA code
                        //label7.Text = Ozy_FPGA_version.ToString();  // Version number of Ozy FPGA code
                    }
                    if (loop_count < 100)
                        loop_count++;
                }

                // get the I, and Q data from rbuf, convert to float, & put into SignalBuffer.cpx for DSP processing
                for (int i = 8; i < 512; i += 8)
                {
                    int k = coarse_pointer + i;
                    // get the I, Q, and mic bytes and form the integer data...JAM

                    // use the following rather than BitConverter.ToInt32 since uses much less CPU

                    // get an I sample...JAM
                    SignalBuffer.cpx[sample_no].real = scaleIn * (float)((rbuf[k + 2] << 8) | (rbuf[k + 1] << 16) | (rbuf[k] << 24));
                    
                    // get a Q sample
                    SignalBuffer.cpx[sample_no].imag = scaleIn * (float)((rbuf[k + 5] << 8) | (rbuf[k + 4] << 16) | (rbuf[k+3] << 24));
                    
                    // get a microphone sample
                    // Get a microphone sample & store it in a simple buffer.
                    // If EP6 sample rate > 48k, the Mic samples received contain duplicates, which we'll ignore.
                    // Thus the Mic samples are decimated before processing.  
                    // The receive audio is fully processed at the higher sampling rate, and then is decimated.
                    // By the time Mercury_send() is called, both streams are at 48ksps.
                    // When the transmit buffer has reached capacity, all samples are processed, and placed in TransmitAudioRing.
                    // 
                    MicSample += 48000;
                    if (MicSample >= SampleRate)     
                    {
                        MicSample = 0;
                        // place sample value in both real & imaginary parts.
                        // When Mic AGC and speech processing are added, they should be done here, 
                        // working with a stream of real samples.

                        // TransmitFilter.ProcessHilbert() will effect a relative phase shift between the real & imaginary parts.

                        // TransmitFilterBuffer.cpx[MicSampleIn].real = ((rbuf[k + 6] << 8) | (rbuf[k + 7]));
                        // In the following line, need (short) cast to ensure that the result is sign-extended
                        // before being converted to float.
                         float Sample = (float) (MicAGC * MicGain * (double)(short) ((rbuf[k + 7] ) | (rbuf[k + 6] << 8)));
                         if ( (Math.Abs(Sample) > 1.0) && (MicAGC > 0.001) ) MicAGC *= 0.995;
                         if ( (Math.Abs(Sample) < 0.01) && (MicAGC < 100.0) ) MicAGC *= 1.0001;
                         if (Sample > 1.0f) Sample = 1.0f;
                         if (Sample < -1.0f) Sample = -1.0f;
                         if (MicAGC > 10.0) Sample = 0.0f;
                         TransmitFilterBuffer.cpx[MicSampleIn].real = Sample;
                        //TransmitFilterBuffer.cpx[MicSampleIn].real = 10.0f * (float)Math.Sin(2.0 * Math.PI * 468.75 * MicTestSample++ / 48000.0);

                        TransmitFilterBuffer.cpx[MicSampleIn].imag = TransmitFilterBuffer.cpx[MicSampleIn].real;
                        if (MicTestSample >= 1024) MicTestSample = 0;

                        // short Mic = (short)((rbuf[k + 6] << 8) | (rbuf[k + 7]));
                        if (++MicSampleIn == isize)         // If TransmitFilterBuffer is full, process the data
                        {
                            MicSampleIn = 0;
                            TransmitFilter.Process();    // #### was ProcessHilbert()  ####  Apply a transmit bandwidth filter & shift the phase of the imaginary part
                            for (int sample = 0; sample < isize; ++sample)
                                TransmitAudioRing.Write(TransmitFilterBuffer.cpx[sample]);  // Place processed data in ring buffer for Mercury_send()
                        }
                    }
 
                    // This single sample is now complete.
                    // The routine will generate 504 such samples from each rbuf that is processed.
                    // Whenever we've accumulated 1024 samples, process them.
                    sample_no++;            // increment the sample number counter
                    if (sample_no == 1024)
                    {
                        sample_no = 0;
                        read_ready = false;           // just in case we try to display whilst data is changing
                        rcvr.DoDSPProcess(ref SignalBuffer.cpx, ref outbuffer);         // Do all the DSP processing for rcvr

                        // If SampleRate > 48000, decimate by skipping samples as we place them into the AudioRing buffer
                        // since sample rate to Ozy is always 48k.
                        int SampleSpacing = SampleRate / 48000;
                        for (int sample = 0; sample < 1023; sample += SampleSpacing)
                            AudioRing.Write(outbuffer[sample]);

                        read_ready = true;  // flag used to indicate to the graphic display code we have data ready

                    }
                    // If SampleRate is 48000, send a frame to Mercury for every frame read
                    // If SampleRate is 96000, send a frame for every two read (audio is decimated by factor of two)
                    // If SampleRate is 192000, send a frame for every four read (audio is decimated by factor of four)
                    SampleCounter += 48000;
                    if (SampleCounter >= SampleRate)
                    {
                        SampleCounter = 0;
                        bool force = false;   // conventional write to Ozy
                        Mercury_send(force);  // send the data to Mercury via Ozy over USB
                    }
                }
            }
        }

        [FlagsAttribute]
        enum C1Bits
        {
            TenMHzAtlas = 0x00,
            TenMHzPenelope = 0x04,
            TenMHzMercury = 0x08,
            Clock122Mercury = 0x10,
            PenelopePresent = 0x20,
            MercuryPresent = 0x40,
            MicPenelope = 0x80,
        }

        // Send four frames of 512 bytes to Ozy / Mercury 
        // The left & right audio to be sent comes from AudioRing buffer, which is filled by ProcessData()
        private void Mercury_send(bool force)
        {
            #region how Mercury_send works
            /*
             send data frames to Mercury
             frames consist of 512 bytes as follows:
             <0x7f><0x7f><0x7f><C0><C1><C2><C3><C4><Left><Left><Right><Right><I><I><Q><Q><Left>.....
            
             if CO = 0x00 then set C1 = 0x58 OR'ed with Sampling rate
             if C0 = 0x02 then C1..C4 hold frequency in Hz

             C0 bit 0 is "MOX"
             When C0 bits 7-1 are zero--
             C1 bits
             7 Mic source: 0 = Janus, 1 = Penelope
             6,5 boards present:  00 neither, 01 penelope, 10 mercury, 11 both
             4   122.88MHz source: 0 penelope, 1 mercury
             3,2 10MHz ref source: 00 atlas, 01 penelope, 10 mercury
             1,0 sampling rate:    00 48kHz, 01 96kHz, 10 192 kHz
             Add up the appropriate ones of the following to create the proper value of C1 to send.
             TODO: need a good way for the user to specify hardware configuration

             C2 bits
             7-1 penelope open collector outputs 6-0
             0   xmt mode: 1 class E, 0 all other modes

             C3 bits
             7  Alex Rx out: 0 off, 1 on
             6,5  Alex Rx antenna: 00 none, 01 Rx1, 10 Rx2, 11 XV
             4    Mercury ADC random: 0 off, 1 on
             3    Mercury ADC dither:  0 off, 1 on
             2    Mercury preamp: 0 off, 1 on
             1,0  Alex attenuator: 00 0dB, 01 10dB, 10 20dB, 11 30dB

             C4 bits
             7-2  unused
             1,0  Alex Tx relay: 00 Tx1, 01 Tx2, 10 Tx3

             * For a full description of the USB protocol see the document in \trunk\Documents
             * if the force flag is set then we send C&C data to Ozy even if we don't have 
             * valid I&Q or left and right audio. This is so that Ozy knows what clocks to use
             * and will start sending data
             *
             */
            #endregion

            // if force is set then send C&C anyway even if no data available
            if (!force)
                if (AudioRing.Count < 252) return;  // need enough data for 4 frames 

            float SampleReal = 1.0f;
            float SampleImag = 1.0f;
            float scaleOut = (float)Math.Pow(2, 15);  // scale factor to convert DSP output to 16-bit short int audio output sample
            byte[] to_Mercury = new byte[2048];       // array to send to Mercury via USB bulk write
            byte[] I_data = new byte[4];
            byte[] Q_data = new byte[4];
            int frame_number = 0;
            int pntr;
            // send 4 frames of 512 bytes for a total of 2048 bytes to Mercury via a USB bulk write...JAM
            for (frame_number = 0; frame_number < 4; frame_number++)
            {
                // toggle C0 between 0x00 and 0x02 each time we send a frame to Ozy
                if (C0 == 0x02) // toggle C0 and send Command & Control data             
                {
                    C0 = 0x00; C1 = 0x00; C2 = 0x00; C4 = 0x00;
                    if (PenneyPresent && (Atlas10MHz || Excalibur))
                    {
                        C1 = (byte)(C1Bits.MicPenelope | C1Bits.MercuryPresent | C1Bits.PenelopePresent | C1Bits.Clock122Mercury | C1Bits.TenMHzAtlas);
                    }
                    else if (PenneyPresent && Penelope10MHz)
                    {
                        C1 = (byte)(C1Bits.MicPenelope | C1Bits.MercuryPresent | C1Bits.PenelopePresent | C1Bits.Clock122Mercury | C1Bits.TenMHzPenelope);
                    }
                    else if (PenneyPresent && Mercury10MHz)
                    {
                        C1 = (byte)(C1Bits.MicPenelope | C1Bits.MercuryPresent | C1Bits.PenelopePresent | C1Bits.Clock122Mercury | C1Bits.TenMHzMercury);
                    }

                    else if (Atlas10MHz || Excalibur)
                    {
                        C1 = (byte)(C1Bits.MercuryPresent | C1Bits.Clock122Mercury | C1Bits.TenMHzAtlas);
                    }

                    else // must be Mercury and Mercury10MHz
                    {
                        C1 = (byte)(C1Bits.MercuryPresent | C1Bits.Clock122Mercury | C1Bits.TenMHzMercury);
                    }

                    // set sampling rate bits in C1
                    switch (SampleRate)
                    {
                        case 480000: break;
                        case 96000:
                            C1 = (byte)(C1 | 0x01);
                            break;
                        case 192000:
                            C1 = (byte)(C1 | 0x02);
                            break;
                    }
                    if (PreampOn)
                        C3 = 0x04;
                    else
                        C3 = 0x00;

                    // check if Penelope Open Collector outputs are enabled, if so set outputs as per user selection
                    if (PennyOC)
                    {
                        // find what band we are using and if receive or transmit
                        switch (BandText)
                        {
                            case "160m": C2 = (byte)(PTT ? Penny160mTxOC : Penny160mRxOC); break;
                            case "80m": C2 = (byte)(PTT ? Penny80mTxOC : Penny80mRxOC); break;
                            case "60m": C2 = (byte)(PTT ? Penny60mTxOC : Penny60mRxOC); break;
                            case "40m": C2 = (byte)(PTT ? Penny40mTxOC : Penny40mRxOC); break;
                            case "30m": C2 = (byte)(PTT ? Penny30mTxOC : Penny30mRxOC); break;
                            case "20m": C2 = (byte)(PTT ? Penny20mTxOC : Penny20mRxOC); break;
                            case "17m": C2 = (byte)(PTT ? Penny17mTxOC : Penny17mRxOC); break;
                            case "15m": C2 = (byte)(PTT ? Penny15mTxOC : Penny15mRxOC); break;
                            case "12m": C2 = (byte)(PTT ? Penny12mTxOC : Penny12mRxOC); break;
                            case "10m": C2 = (byte)(PTT ? Penny10mTxOC : Penny10mRxOC); break;
                            case "6m": C2 = (byte)(PTT ? Penny6mTxOC : Penny6mRxOC); break;
                            case "GC":
                            default: C2 = 0x00; break;
                        }
                    }
                    else
                    {
                        C2 = 0x00;  // default Open Collector outputs to off
                    }
                }
                else
                {
                    // toggle CO from 0 to 0x02 and send frequency
                    C0 = 0x02; C1 = frequency[3]; C2 = frequency[2]; C3 = frequency[1]; C4 = frequency[0]; // send frequency data
                }

                if (((MOX_set || PTT) && KK_on) ||(!OnlyTxOnPTT && KK_on)) 
                {
                    C0 += 1;   // set PTT true
                }

                pntr = frame_number * 512;
                to_Mercury[pntr] = sync; to_Mercury[pntr + 1] = sync; to_Mercury[pntr + 2] = sync;
                to_Mercury[pntr + 3] = C0; to_Mercury[pntr + 4] = C1; to_Mercury[pntr + 5] = C2;
                to_Mercury[pntr + 6] = C3; to_Mercury[pntr + 7] = C4;

                for (int x = 8; x < 512; x += 8)        // fill out one 512-byte frame
                {
                    AudioRing.Read(ref SampleReal, ref SampleImag);

                    // use the following rather than BitConverter.GetBytes since it uses less CPU 
                    int IntValue;
                    IntValue = (int)(scaleOut * SampleReal);
                    to_Mercury[pntr + x] = (byte)(IntValue >> 8);        // left hi
                    to_Mercury[pntr + x + 1] = (byte)(IntValue & 0xff);  // left lo
                    IntValue = (int)(scaleOut * SampleImag);
                    to_Mercury[pntr + x + 2] = (byte)(IntValue >> 8);    // right hi
                    to_Mercury[pntr + x + 3] = (byte)(IntValue & 0xff);  // right lo

                    // I&Q data to Ozy for Penny!
                    short A, B;
                    // TxGain is the product of the Drive setting and the Gain per band setting
                    float TxGain = DriveGain * (float)(BandGain / 100.0f);

                    // send a Tune tone if TUN button is pressed
                    if (TUN_set)
                    {
                        switch (ModeText)
                        {
                            case "USB":
                            case "CWU":
                            case "AM":
                            case "FM":
                                {
                                    A = (short)(TxGain * CWnoteSin[CWnoteIndex]);  // TxGain sets output level
                                    B = (short)(TxGain * CWnoteCos[CWnoteIndex]);
                                    if (++CWnoteIndex >= 80) CWnoteIndex = 0;
                                    to_Mercury[pntr + x + 4] = (byte)(A >> 8);      // I_data[0];
                                    to_Mercury[pntr + x + 5] = (byte)(A & 0xff);    // I_data[1];
                                    to_Mercury[pntr + x + 6] = (byte)(B >> 8);      // Q_data[0];
                                    to_Mercury[pntr + x + 7] = (byte)(B & 0xff);    // Q_data[1];
                                    break;
                                }
                            case "LSB":  // need to swap the phase of I&Q if LSB signal
                            case "CWL":
                                {
                                    A = (short)(TxGain * CWnoteCos[CWnoteIndex]);   // TxGain sets output level
                                    B = (short)(TxGain * CWnoteSin[CWnoteIndex]);
                                    if (++CWnoteIndex >= 80) CWnoteIndex = 0;
                                    to_Mercury[pntr + x + 4] = (byte)(A >> 8);      // I_data[0];
                                    to_Mercury[pntr + x + 5] = (byte)(A & 0xff);    // I_data[1];
                                    to_Mercury[pntr + x + 6] = (byte)(B >> 8);      // Q_data[0];
                                    to_Mercury[pntr + x + 7] = (byte)(B & 0xff);    // Q_data[1];
                                    break;
                                }
                        }
                    }
                    else
                    {
                        if (!MOX_set)
                        {
                            TxGain = 0.0f;  // Set I&Q to zero if MOX not set 
                        }

                        // convert mic data from float (+/- 1) to 2's complement and set drive level
                        float MicScaleOut = (float)Math.Pow(2, 14) * TxGain;
                        switch (ModeText)
                        {
                            case "USB":
                                if (TransmitAudioRing.Count > 0)
                                {
                                    TransmitAudioRing.Read(ref SampleReal, ref SampleImag);

                                    A = (short)(MicScaleOut * SampleReal);
                                    B = (short)(MicScaleOut * SampleImag);

                                    to_Mercury[pntr + x + 4] = (byte)(A >> 8);      // I_data[0];
                                    to_Mercury[pntr + x + 5] = (byte)(A & 0xff);    // I_data[1];
                                    to_Mercury[pntr + x + 6] = (byte)(B >> 8);       // Q_data[0];
                                    to_Mercury[pntr + x + 7] = (byte)(B & 0xff);    // Q_data[1];
                                }
                                break;
                            case "LSB":
                                if (TransmitAudioRing.Count > 0)
                                {
                                    TransmitAudioRing.Read(ref SampleReal, ref SampleImag);

                                    B = (short)(MicScaleOut * SampleReal);
                                    A = (short)(MicScaleOut * SampleImag);
                                    to_Mercury[pntr + x + 4] = (byte)(A >> 8);      // I_data[0];
                                    to_Mercury[pntr + x + 5] = (byte)(A & 0xff);    // I_data[1];
                                    to_Mercury[pntr + x + 6] = (byte)(B >> 8);      // Q_data[0];
                                    to_Mercury[pntr + x + 7] = (byte)(B & 0xff);    // Q_data[1];
                                }
                                break;
                            case "CWU":
                                A = (short)(TxGain * CWnoteSin[CWnoteIndex]);  // TxGain sets output level
                                B = (short)(TxGain * CWnoteCos[CWnoteIndex]);
                                if (++CWnoteIndex >= 80) CWnoteIndex = 0;
                                to_Mercury[pntr + x + 4] = (byte)(A >> 8);      // I_data[0];
                                to_Mercury[pntr + x + 5] = (byte)(A & 0xff);    // I_data[1];
                                to_Mercury[pntr + x + 6] = (byte)(B >> 8);      // Q_data[0];
                                to_Mercury[pntr + x + 7] = (byte)(B & 0xff);    // Q_data[1];
                                break;
                            case "CWL":
                                A = (short)(TxGain * CWnoteCos[CWnoteIndex]);   // TxGain sets output level
                                B = (short)(TxGain * CWnoteSin[CWnoteIndex]);
                                if (++CWnoteIndex >= 80) CWnoteIndex = 0;
                                to_Mercury[pntr + x + 4] = (byte)(A >> 8);      // I_data[0];
                                to_Mercury[pntr + x + 5] = (byte)(A & 0xff);    // I_data[1];
                                to_Mercury[pntr + x + 6] = (byte)(B >> 8);      // Q_data[0];
                                to_Mercury[pntr + x + 7] = (byte)(B & 0xff);    // Q_data[1];
                                break;
                            case "AM":
                                IntValue = 0;
                                break;
                            default:
                                IntValue = 0;
                                break;
                        }
                    }
                }
            }
            if (KK_on)
            {
                int ret;
                //do
                //{
                    ret = libUSB_Interface.usb_bulk_write(hdev, 0x02, to_Mercury, 2048, 100); //69ingChipmunks
                //} while (ret != 2048);
                // TODO: Need to add error routine if write to USB fails
                    if (ret != 2048)
                    {
                        Debug.WriteLine("Write to Ozy failed - returned \t" + ret);
                    }
            }
        }

        // Slider control sets transceiver center frequency
        private void set_frequency_Scroll_1(object sender, EventArgs e)
        {
            // when the frequency slider has focus the mouse scroll wheel
            // can be used to increment the frequency by +/- step_size.

            // the scroll bar can also be moved with the arrow and Up/Down keys 
            // make the scroll bar large and small step sizes a function of the step size 
            set_frequency.LargeChange = ((step_size + 1) * 100) - step_size;
            set_frequency.SmallChange = (step_size + 1);
            // check we are still within the band and update the tune and display frequencies
            Frequency_change();
        }

        // select the step size that the mouse scroll wheel does
        private void stepSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            // since we have alreade taken a step use (step size - 1)
            // TODO: this works OK for the mousewheel on my mouse but check this is universal
            switch (stepSize.Text)
            {
                case "1Hz": step_size = 0; break;
                case "10Hz": step_size = 9; break;
                case "100Hz": step_size = 99; break;
                case "1kHz": step_size = 999; break;
            }
            set_frequency.Focus(); // return focus to the frequency slider
        }

        public void UpdateBandGain()
        {
            switch (BandSelect.Text)
            {
                case "160m":
                    BandGain = Gain160m; // set the band gain
                    output_band = "160m";
                    break;
                case "80m":
                    BandGain = Gain80m; // set the band gain 
                    output_band = "80m";
                    break;
                case "40m":
                    BandGain = Gain40m; // set the band gain 
                    output_band = "40m";
                    break;
                case "30m":
                    BandGain = Gain30m; // set the band gain 
                    output_band = "30m";
                    break;
                case "20m":
                    BandGain = Gain20m; // set the band gain 
                    output_band = "20m";
                    break;
                case "17m":
                    BandGain = Gain17m; // set the band gain 
                    output_band = "17m";
                    break;
                case "15m":
                    BandGain = Gain15m; // set the band gain 
                    output_band = "15m";
                    break;
                case "12m":
                    BandGain = Gain12m; // set the band gain 
                    output_band = "12m";
                    break;
                case "10m":
                    BandGain = Gain10m; // set the band gain 
                    output_band = "10m";
                    break;
                case "6m":
                    BandGain = Gain6m; // set the band gain
                    output_band = "6m";
                    break;
                    
            }
           
           // create_connection();
        }

        public void BandSelect_SelectedIndexChanged(object sender, EventArgs e)
        {
            // when changing bands store the last frequency used on that band so we can return to it later
            switch (BandText)
            {
                case "160m": set_frequency_160 = set_frequency.Value; break;
                case "80m": set_frequency_80 = set_frequency.Value; break;
                case "40m": set_frequency_40 = set_frequency.Value; break;
                case "30m": set_frequency_30 = set_frequency.Value; break;
                case "20m": set_frequency_20 = set_frequency.Value; break;
                case "17m": set_frequency_17 = set_frequency.Value; break;
                case "15m": set_frequency_15 = set_frequency.Value; break;
                case "12m": set_frequency_12 = set_frequency.Value; break;
                case "10m": set_frequency_10 = set_frequency.Value; break;
                case "6m": set_frequency_6 = set_frequency.Value; break;
                case "GC": set_frequency_GC = set_frequency.Value; break;
            }

            // set the band edges depending on the band selected. Restore the last used frequency from KK.csv
            switch (BandSelect.Text)
            {
                case "160m":
                    set_frequency.Minimum = 1800000;  // low band limit
                    set_frequency.Maximum = 2000000;  // high band limit
                    set_frequency.Value = set_frequency_160; // last used frequency from KK.csv
                    if (Mode.Text == "USB")
                    {
                        Mode.Text = "LSB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_160;
                    BandGain = Gain160m; // set the band gain 
                    break;
                case "80m":
                    set_frequency.Minimum = 3500000;
                    set_frequency.Maximum = 4000000;
                    set_frequency.Value = set_frequency_80;
                    if (Mode.Text == "USB")
                    {
                        Mode.Text = "LSB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_80;
                    BandGain = Gain80m;
                    break;
                case "40m":
                    set_frequency.Minimum = 7000000;
                    set_frequency.Maximum = 7300000;
                    set_frequency.Value = set_frequency_40;
                    if (Mode.Text == "USB")
                    {
                        Mode.Text = "LSB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_40;
                    BandGain = Gain40m;
                    break;
                case "30m":
                    set_frequency.Minimum = 10100000;
                    set_frequency.Maximum = 10150000;
                    set_frequency.Value = set_frequency_30;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_30;
                    BandGain = Gain30m;
                    break;
                case "20m":
                    set_frequency.Minimum = 14000000;
                    set_frequency.Maximum = 14350000;
                    set_frequency.Value = set_frequency_20;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_20;
                    BandGain = Gain20m;
                    break;
                case "17m":
                    set_frequency.Minimum = 18068000;
                    set_frequency.Maximum = 18168000;
                    set_frequency.Value = set_frequency_17;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_17;
                    BandGain = Gain17m;
                    break;
                case "15m":
                    set_frequency.Minimum = 21000000;
                    set_frequency.Maximum = 21450000;
                    set_frequency.Value = set_frequency_15;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_15;
                    BandGain = Gain15m;
                    break;
                case "12m":
                    set_frequency.Minimum = 24880000;
                    set_frequency.Maximum = 24980000;
                    set_frequency.Value = set_frequency_12;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_12;
                    BandGain = Gain12m;
                    break;
                case "10m":
                    set_frequency.Minimum = 28000000;
                    set_frequency.Maximum = 29700000;
                    set_frequency.Value = set_frequency_10;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_10;
                    BandGain = Gain10m;
                    break;
                case "6m":
                    set_frequency.Minimum = 50000000;
                    set_frequency.Maximum = 55000000;
                    set_frequency.Value = set_frequency_6;
                    if (Mode.Text == "LSB")
                    {
                        Mode.Text = "USB";                  // select the appropriate sideband
                        ChangeMode();  // reset the Bandwidth slider
                    }
                    Preamp.Checked = Preamp_6;
                    BandGain = Gain6m;
                    break;
                case "GC":
                    set_frequency.Minimum = 0;
                    set_frequency.Maximum = 55000000;
                    set_frequency.Value = set_frequency_GC;
                    Preamp.Checked = Preamp_GC;
                    BandGain = 0;
                    break;
                default:
                    set_frequency.Minimum = 14000000;
                    set_frequency.Maximum = 14350000;
                    break;
            }
            BandText = BandSelect.Text; // save the band we are currently on as we leave 
            output_band = BandSelect.Text;
            Frequency_change(); // update frequency and display
            set_frequency.Focus(); // return focus to the frequency slider
        }

        private void Mode_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeMode(); // select filter setting based on mode
            set_frequency.Focus(); // return focus to the frequency slider
        }

        private void ChangeMode() // ChangeMode() is called by Mode/BandSelect_SelectedIndexChanged()
        {
            // depending on the Mode selected set the upper and low filter selection frequencies and the bandwidth
            ModeText = Mode.Text;

            switch (Mode.Text)
            {
                case "AM":  // this is 'regular' AM, not Synchronous AM (SharpDSP2._1.DSPMode_e.SAM)
                    state.DSPMode = SharpDSP2._1.DSPMode_e.AM;
                    BandwidthTrackBar.Minimum = 3000;
                    BandwidthTrackBar.Maximum = 12000;
                    rcvr.FilterFrequencyHigh = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyLow = -BandwidthTrackBar.Value;
                    break;

                case "FM":
                    rcvr.SetFMMode();
                    state.DSPMode = SharpDSP2._1.DSPMode_e.FMN;
                    BandwidthTrackBar.Minimum = 3000;
                    BandwidthTrackBar.Maximum = 12000;
                    rcvr.FilterFrequencyHigh = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyLow = -BandwidthTrackBar.Value;
                    break;

                case "SAM":
                    rcvr.SetSAMMode();
                    state.DSPMode = SharpDSP2._1.DSPMode_e.SAM;
                    BandwidthTrackBar.Minimum = 3000;
                    BandwidthTrackBar.Maximum = 12000;
                    rcvr.FilterFrequencyHigh = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyLow = -BandwidthTrackBar.Value;
                    break;

                case "USB":
                    state.DSPMode = SharpDSP2._1.DSPMode_e.USB;
                    BandwidthTrackBar.Minimum = 1200;
                    BandwidthTrackBar.Maximum = 6000;
                    rcvr.FilterFrequencyHigh = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyLow = 200.0f;
                    break;

                case "LSB":
                    state.DSPMode = SharpDSP2._1.DSPMode_e.LSB;
                    BandwidthTrackBar.Minimum = 1200;
                    BandwidthTrackBar.Maximum = 6000;
                    rcvr.FilterFrequencyHigh = -BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyLow = -200.0f;
                    break;

                case "CWL":
                    state.DSPMode = SharpDSP2._1.DSPMode_e.CWL;
                    BandwidthTrackBar.Minimum = 100;
                    BandwidthTrackBar.Maximum = 1100;
                    rcvr.FilterFrequencyHigh = -(CWPitch + BandwidthTrackBar.Value / 2);
                    rcvr.FilterFrequencyLow = -(CWPitch - BandwidthTrackBar.Value / 2);
                    break;

                case "CWU":
                    state.DSPMode = SharpDSP2._1.DSPMode_e.CWU;
                    BandwidthTrackBar.Minimum = 100;
                    BandwidthTrackBar.Maximum = 1100;
                    rcvr.FilterFrequencyHigh = (CWPitch + BandwidthTrackBar.Value / 2);
                    rcvr.FilterFrequencyLow = (CWPitch - BandwidthTrackBar.Value / 2);
                    break;
            }
        }

        // set the volume based on the VolumeTrackBar value
        public void SetVolume(object sender, EventArgs e)
        {
            // set the Receiver volume level
            setVolumeSquelched();
            set_frequency.Focus(); // return focus to the frequency slider
        }

        public void setVolumeSquelched()
        {
            // if squelch it turned on, AND 'squelched' is true, OR MOX is sent AND not full Duplex then mute audio
            if ((squelchOn && squelched) || (MOX_set && !Duplex))
            {
                rcvr.VolumeLeft = 0;  // send to SharpSDP
                rcvr.VolumeRight = 0;
            }
            else
            {
                // otherwise set to volume trackbar level
                // convert the track bar value to a float in the range 0 to 1, since that's what SharpDSP needs
                float v = (float)VolumeTrackBar.Value / (float)VolumeTrackBar.Maximum;
                rcvr.VolumeLeft = v;  // send to SharpSDP
                rcvr.VolumeRight = v;
           }
        }

        // set the AGC Gain  based on the AGC TrackBar value
        private void AGCTrackBar_Scroll(object sender, EventArgs e)
        {
            rcvr.AGCMaximumGainDB = AGCTrackBar.Value;  // TODO: make this value user adjustable and store/restore on KK.cvs
            rcvr.AGCFixedGainDB = AGCTrackBar.Value;    // ditto

            set_frequency.Focus(); // return focus to the frequency slider
        }

        public SharpDSP2._1.AGCMethod AgcMethod
        {
            get { return rcvr.AGCMethod; }
            set { rcvr.AGCMethod = value; }
        }

        public float AGCFixedGainDB
        {
            get { return rcvr.AGCFixedGainDB; }
            set
            {
                rcvr.AGCFixedGainDB = value;
            }
        }

        public float AGCMaximumGainDB
        {
            get { return rcvr.AGCMaximumGainDB; }
            set
            {
                rcvr.AGCMaximumGainDB = value;
            }
        }

        public float AGCHangTime
        {
            get { return rcvr.AGCHangTime; }
            set
            {
                rcvr.AGCHangTime = value;
            }
        }

        public float AGCHangThreshold
        {
            get { return rcvr.AGCHangThres; }
            set
            {
                rcvr.AGCHangThres = value;
            }
        }

        public float AGCAttackTime
        {
            get { return rcvr.AGCAttackTime; }
            set
            {
                rcvr.AGCAttackTime = value;
            }
        }

        public float AGCSlope
        {
            get { return rcvr.AGCSlope; }
            set
            {
                rcvr.AGCSlope = value;
            }
        }

        public float AGCFastAttackTime
        {
            get { return rcvr.AGCFastAttackTime; }
            set
            {
                rcvr.AGCFastAttackTime = value;
            }
        }

        public float AGCFastDecayTime
        {
            get { return rcvr.AGCFastDecayTime; }
            set
            {
                rcvr.AGCFastDecayTime = value;
            }
        }

        // Set the AGC speed based on the user selection 
        private void AGCSpeed_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (AGCSpeed.Text)
            {
                case "OFF": rcvr.AGCMode = SharpDSP2._1.AGCType_e.agcOff; break;
                case "Long": rcvr.AGCMode = SharpDSP2._1.AGCType_e.agcLong; break;
                case "Slow": rcvr.AGCMode = SharpDSP2._1.AGCType_e.agcSlow; break;
                case "Med": rcvr.AGCMode = SharpDSP2._1.AGCType_e.agcMedium; break;
                case "Fast": rcvr.AGCMode = SharpDSP2._1.AGCType_e.agcFast; break;
                case "User": rcvr.AGCMode = SharpDSP2._1.AGCType_e.agcUser; break;
            }

            UpdateAGCTimeDisplay();

            set_frequency.Focus(); // return focus to the frequency slider
        }

        private void UpdateAGCTimeDisplay()
        {
            numericUpDownAGCTime.Value = Convert.ToDecimal(rcvr.AGCDecayTime);
        }

        // select the appropriate filter bandwith for the Mode slected 
        public void AdjustFilterByMode()
        {
            TransmitFilter.FilterFrequencyLow = TxFilterLow;
            TransmitFilter.FilterFrequencyHigh = TxFilterHigh;      // Force re-calculation of filter coefficients (esp after change in SampleRate)
            switch (Mode.Text)
            {
                case "LSB":
                    int BandwidthSBvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = -BandwidthSBvalue;
                    rcvr.FilterFrequencyLow = -200.0f;
                    break;
                case "USB":
                    BandwidthSBvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyLow = 200.0f;
                    break;
                case "CWL":
                    int BandwidthCWvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = -(CWPitch + BandwidthCWvalue / 2);
                    rcvr.FilterFrequencyLow = -(CWPitch - BandwidthCWvalue / 2);
                    break;
                case "CWU":
                    BandwidthCWvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = (CWPitch + BandwidthCWvalue / 2);
                    rcvr.FilterFrequencyLow = (CWPitch - BandwidthCWvalue / 2);
                    break;
                case "AM":
                    int BandwidthAMvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = BandwidthAMvalue;
                    rcvr.FilterFrequencyLow = -BandwidthAMvalue;
                    break;
                case "SAM":
                    int BandwidthSAMvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = BandwidthSAMvalue;
                    rcvr.FilterFrequencyLow = -BandwidthSAMvalue;
                    break;
                case "FM":
                    int BandwidthFMvalue = BandwidthTrackBar.Value;
                    rcvr.FilterFrequencyHigh = BandwidthFMvalue;
                    rcvr.FilterFrequencyLow = -BandwidthFMvalue;
                    break;
            }
        }

        // Vary the filter Bandwidth when the Bandwidth control is changed
        private void BandwidthTrackBar_Scroll(object sender, EventArgs e)
        {
            AdjustFilterByMode();
            set_frequency.Focus(); // return focus to the frequency slider
        }

        public int   aNFAdaptiveFilterSize = 64;
        public int ANFAdaptiveFilterSize
        {
            get { return aNFAdaptiveFilterSize; }
            set { aNFAdaptiveFilterSize = value; rcvr.InterferenceFilterAdaptiveFilterSize = value; }
        }

        public int   aNFDelay = 12;
        public int ANFDelay
        {
            get { return aNFDelay; }
            set { aNFDelay = value; rcvr.InterferenceFilterDelay = value; }
        }

        public float aNFAdaptationRate = .005F;
        public float ANFAdaptationRate
        {
            get { return aNFAdaptationRate; }
            set { aNFAdaptationRate = value; rcvr.InterferenceFilterAdaptationRate = value; }
        }

        public float aNFLeakage = .01F;
        public float ANFLeakage
        {
            get { return aNFLeakage; }
            set { aNFLeakage = value; rcvr.InterferenceFilterLeakage = value; }
        }

        // set up the Automatic Notch Filter
        private void ANF_CheckedChanged(object sender, EventArgs e)
        {
            if (ANF.Checked) 
            {
                rcvr.InterferenceFilterSwitchOn = true;
                rcvr.InterferenceFilterAdaptiveFilterSize = ANFAdaptiveFilterSize;
                rcvr.InterferenceFilterDelay = ANFDelay;
                rcvr.InterferenceFilterAdaptationRate = ANFAdaptationRate;
                rcvr.InterferenceFilterLeakage = ANFLeakage;
            }
            else
                rcvr.InterferenceFilterSwitchOn = false;

            set_frequency.Focus(); // return focus to the frequency slider
        }

        public int nRAdaptiveFilterSize = 64;
        public int NRAdaptiveFilterSize
        {
            get { return nRAdaptiveFilterSize; }
            set { nRAdaptiveFilterSize = value; rcvr.NoiseFilterAdaptiveFilterSize = value; }
        }

        public int nRDelay = 12;
        public int NRDelay
        {
            get { return nRDelay; }
            set { nRDelay = value; rcvr.NoiseFilterDelay = value; }
        }

        public float nRAdaptationRate = .005F;
        public float NRAdaptationRate
        {
            get { return nRAdaptationRate; }
            set { nRAdaptationRate = value; rcvr.NoiseFilterAdaptationRate = value; }
        }

        public float nRLeakage = .01F;
        public float NRLeakage
        {
            get { return nRLeakage; }
            set { nRLeakage = value; rcvr.NoiseFilterLeakage = value; }
        }

        // set up the Noise Reduction 
        private void NR_CheckedChanged(object sender, EventArgs e)
        {
            if (NR.Checked)  
            {
                rcvr.NoiseFilterSwitchOn = true;
                rcvr.NoiseFilterAdaptiveFilterSize = NRAdaptiveFilterSize;
                rcvr.NoiseFilterDelay = NRDelay;
                rcvr.NoiseFilterAdaptationRate = NRAdaptationRate;
                rcvr.NoiseFilterLeakage = NRLeakage;
            }
            else
                rcvr.NoiseFilterSwitchOn = false;

            set_frequency.Focus(); // return focus to the frequency slider
        }

        // Noise Blanker1
        private void NB1_CheckedChanged(object sender, EventArgs e)
        {
            rcvr.BlockNBSwitchOn = NB1.Checked;
            set_frequency.Focus(); // return focus to the frequency slider
        }

        // Noise Blanker2
        private void NB2_CheckedChanged(object sender, EventArgs e)
        {
            rcvr.AveNBSwitchOn = NB2.Checked;
            set_frequency.Focus(); // return focus to the frequency slider
        }

        private void UpdateGraphBackgrounds()
        {
            updateSpectrumGraphBackground = true;
            updateWidebandGraphBackground = true;
        }

        private void Frequency_change()
        {
            UpdateGraphBackgrounds();

            // we need to check if we are too close to the band edge to take a step 
            if (set_frequency.Value > previous_value && set_frequency.Value < (set_frequency.Maximum - step_size))
                set_frequency.Value += step_size;  // add step_size since scroll wheel has added 1 already
            else if (set_frequency.Value < previous_value && set_frequency.Value > (set_frequency.Minimum + step_size + 1))
                set_frequency.Value -= step_size; // subtract step_size since scroll wheel has subtracted 1 already
            // else use the current value

            // round frequency to step size
            int temp_frequency = set_frequency.Value; // use a temp value so we don't have to worry about the band edges.
            temp_frequency = temp_frequency / (step_size + 1);
            temp_frequency = temp_frequency * (step_size + 1);
            set_frequency.Value = temp_frequency;

            // display the new frequency and send to Ozy
            DisplayFrequency(set_frequency.Value);
            // save current value so we can compare with next value so we can determine tune direction
            previous_value = set_frequency.Value;
           
        }


        // when ever timer1 fires we update the bandscope
        private void timer1_Tick(object sender, EventArgs e)
        {


            //Rate sets the display FPS rate, its located on the Setup form
            if (Rate <= 0)
                timer1.Interval = 2000;
            else
                timer1.Interval = 1000 / Rate;  //timer value from 1000 to 50 i.e. 1 second to 20mS

            // display the status of Sync and ADC overload on the 'LEDs'
            SyncLED.BackColor = IsSynced ? Color.Green : Color.Red;
            ADCoverloadButton.BackColor = ADCOverload ? Color.Red : SystemColors.Control;

            // if PTT has changed state then process the new state 
            if (PTT != Previous_PTT)
            {
                // we have a change in PTT so call MOX method and set accordingly
                MOX_Click(this, EventArgs.Empty); 
                Previous_PTT = PTT; // store the current state
            }           

            if (read_ready)  // update the display if data is available 
            {
                read_ready = false;

                // get a wide-bandwidth sample
                ProcessWideBandData();

                // get bandwidth samples as IQ data
                GetIQSamples();

                ComputeDisplay();

                Squelch(); 

                if (chkSpec.Checked || ShowI || ShowQ)
                {
                    useSpectrumBackground = true;

                    pictureBoxSpectrum.Invalidate(); // causes RefreshScope to run 
                }
                else
                {
                    useSpectrumBackground = false;
                    pictureBoxSpectrum.BackgroundImage = null;
                }

                if (chkWaterFall.Checked || chkWideSpec.Checked)
                {
                    if (chkWaterFall.Checked)
                    {
                        useWidebandBackground = false;
                        pictureBoxWideband.BackgroundImage = null;
                    }
                    if (chkWideSpec.Checked)
                    {
                        useWidebandBackground = true;
                    }

                    pictureBoxWideband.Invalidate(); // causes RefreshScope to run
                }

                // not a cross-thread call when called from the timer tick!
                // display SMeter reading.  Note that this is accumulated by the Receiver object
                // when it is invoked by Process_Data (G Byrkit, K9TRV, 15 June 2009)
                SetSMeterText(SMeterText(rcvr.SMeterAvgValue, rcvr.SMeterInstValue));

            }

           

           
        }

        private bool squelched = false;

        // process bandscope and filter squelch.
        private void Squelch()
        {
            /* 
             * 
             * When using the filter width squelch we need to compenstate for the fact that the bandwidth is wider than
             * that when using the bandscope width. The FFT bin width (Hz) for the bandscope is 
             * 
             *      bandwidth =  sampling rate / FFT size 
             *      
             * For the filter bandwidth we can just read the filter slider.
             * Hence the  noise power will be higher in the filter bandwidth by 
             * 
             *       ratio = filter bandwidth/(sampling rate/FFT size)
             *       
             * Since we are working in dB power levels we need to take 10log of this value and apply to the signal level
             * 
             */
                        
            // if squelch is not selected then just return
            if (!Bandscope_squelch.Checked && !Filter_squelch.Checked)
            {
                Squelch_level.BackColor = SystemColors.Control;
                return;
            }

            int BandWidthCorrection = 10 * (int)Math.Log10(BandwidthTrackBar.Value / (SampleRate / 1024));

            // Do Bandscope squelch
            if (Bandscope_squelch.Checked && (max_signal < Squelch_level.Value/10))
            {
                    squelched = true;
                    Squelch_level.BackColor = Color.Yellow; // change the slider background to show we have muted the audio 
            }

            // Do filter squelch - this uses the average SMeter value as its level
            else if (Filter_squelch.Checked && (filter_max_signal < ((Squelch_level.Value / 10) + BandWidthCorrection)))
            {
                squelched = true;
                Squelch_level.BackColor = Color.Yellow; // change the slider background to show we have muted the audio 
            }
            else  // use the user volume setting 
            {
                squelched = false; 
                Squelch_level.BackColor = Color.Green; // change the slider background to show are above threshold
            }

            setVolumeSquelched();
        }

        // Get ozy firmware version string - 8 bytes,  returns null for error - thanks to Bill KD5TFD for this code
        private string getOzyFirmwareString()
        {
            if (hdev.ToInt32() == 0)
            {
                return null;
            }

            // the following set of declarations MUST match the values used in the FX2 code - hpsdr_commands.h
            byte VRQ_SDR1K_CTL = 0x0d;
            byte SDR1KCTRL_READ_VERSION = 0x7;
            byte VRT_VENDOR_IN = 0xC0;
            byte[] buf = new byte[8];
            int rc = libUSB_Interface.usb_control_msg(hdev, VRT_VENDOR_IN, VRQ_SDR1K_CTL, SDR1KCTRL_READ_VERSION, 0, buf, buf.Length, 1000); //69ingChipmunks
            Debug.WriteLine("read version rc: " + rc);
            Debug.WriteLine("read version hdev = \t" + hdev);

            string result = null;

            if (rc == 8)    // got length we expected
            {
                char[] cbuf = new char[8];
                for (int i = 0; i < 8; i++)
                {
                    cbuf[i] = (char)(buf[i]);
                }
                result = new string(cbuf);
                Debug.WriteLine("version: >" + result + "<");
            }
            return result;
        }

        
        //public bool PenneyPresent
        //{
        //    get { return penneyPresent; }
        //    set { penneyPresent = value; }
        //}

        private void Start()
        {
                // start the thread that reads the USB port on Ozy
                if (!start_USB())
                {
                    MessageBox.Show("No Ozy board found  - Check HPSDR is connected and powered");
                    OnOffButton_Click(this, EventArgs.Empty); // Toggle ON/OFF Button to OFF
                    return;
                }

                // Check that FX2 has been loaded with software, if not call initozy11.bat to load it
                Ozy_version = getOzyFirmwareString(); // Get ozy firmware version string - 8 bytes,  returns null for error

                if (Ozy_version == null)
                {
                    this.Cursor = Cursors.WaitCursor;  // show hour glass cursor whilst we load Ozy FPGA
                    // call a process to run initozy11.bat in current directory
                    ProcessStartInfo start_info = new ProcessStartInfo();
                    start_info.FileName = "initozy11.bat";
                    start_info.UseShellExecute = true;
                    Process p = new Process();
                    p.StartInfo = start_info;
                    bool rc = p.Start();
                    System.Console.WriteLine("start returned: " + rc);
                    p.WaitForExit();
                    System.Console.WriteLine("OzyInit completes");
                    stop_USB();  // need to close and re-open USB port since it renumerated
                    start_USB();
                    this.Cursor = Cursors.Default;  // revert to normal cursor

                    // get ozy code version now
                    Ozy_version = getOzyFirmwareString();
                }

                // check that we are using the correct version of FX2 code
                if (Ozy_version != "20090524")
                {
                    MessageBox.Show(" Wrong version of Ozy code found " + Ozy_version.ToString() + "\n- should be 20090524");
                    OnOffButton_Click(this, EventArgs.Empty); // Toggle ON/OFF Button to OFF
                    stop_USB();
                    return;
                }

                // If a Penelope board is fitted then configure the TLV320 CODEC via the I2C connection on Atlas
                // The settings are as follows: 
                //    1E 00 - Reset chip
                //    12 01 - set digital interface active
                //    08 15 - D/A on, mic input, mic 20dB boost
                //    08 14 - ditto but no mic boost
                //    0C 00 - All chip power on
                //    0E 02 - Slave, 16 bit, I2S
                //    10 00 - 48k, Normal mode
                //    0A 00 - turn D/A mute off

                if (PenneyPresent)
                {
                    byte[] Penny_TLV320 = new byte[2];
                    byte[] Penny_TLV320_data = new byte[14];

                    // need to select the config data depending on the Mic Gain (20dB) selected
                    if(MicGain20dB)
                        Penny_TLV320_data = new byte[] { 0x1e, 0x00, 0x12, 0x01, 0x08, 0x15, 0x0c, 0x00, 0x0e, 0x02, 0x01, 0x00, 0x0a, 0x00 };
                    else
                        Penny_TLV320_data = new byte[] { 0x1e, 0x00, 0x12, 0x01, 0x08, 0x14, 0x0c, 0x00, 0x0e, 0x02, 0x01, 0x00, 0x0a, 0x00 };
                    // set the I2C interface speed to 400kHZ
                    if (!(OZY.Set_I2C_Speed(hdev, 1)))
                        MessageBox.Show("Unable to set I2C speed to 400kHz", "System Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    // send the configuration data to the TLV320 on Penelope 
                    for (int x = 0; x < 14; x += 2)
                    {
                        Penny_TLV320[0] = Penny_TLV320_data[x]; Penny_TLV320[1] = Penny_TLV320_data[x + 1];
                        if (!(OZY.Write_I2C(hdev, 0x1b, Penny_TLV320)))
                            MessageBox.Show("Unable to configure TLV320 on Penelope via I2C", "System Eror!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }

                Debug.WriteLine("Starting USB Thread");
               
                // start thread to read Ozy data from USB.   USBLoop merely calls Process_Data, 
                // which calls usb_bulk_read() and rcvr.Process(),
                // and stuffs the demodulated audio into AudioRing buffer
                USB_thread = new Thread(new ThreadStart(USBLoop));
                USB_thread.Name = "USB Loop";
                USB_thread.Priority = ThreadPriority.Highest; // run USB thread at high priority
                //USB_thread.IsBackground = true;  // do we need this ?
                USB_thread.Start();
                USB_thread_running = true;
                timer1.Enabled = true;  // start timer for bandscope update.
            }

            private void Stop()
            {
                //TODO:  **** Need to turn MOX off if on and turn PTT relay off  *****
        
                if (USB_thread_running)
                {
                    USB_thread.Abort();  // stop USB thread
                    USB_thread_running = false;
                }
                timer1.Enabled = false;
                stop_USB();          // kill the USB port 
                SyncLED.BackColor = SystemColors.Control;  // no sync so set LED to background
            }
            
        

        private void chkSpec_CheckedChanged(object sender, EventArgs e)
        {
            SetSplitterBar();
            set_frequency.Focus(); // return focus to the frequency slider
        }

        private void chkWideSpec_CheckedChanged(object sender, EventArgs e)
        {
            if (chkWideSpec.Checked)
            {
                chkWaterFall.Checked = false;
            }

            SetSplitterBar();

            set_frequency.Focus(); // return focus to the frequency slider
        }

        bool PreampOn = false;

        private void Preamp_CheckedChanged(object sender, EventArgs e)
        {
            PreampOn = Preamp.Checked;

            // save the preamp setting for each band
            switch (BandSelect.Text)
            {
                case "160m": Preamp_160 = Preamp.Checked; break;
                case "80m": Preamp_80 = Preamp.Checked; break;
                case "40m": Preamp_40 = Preamp.Checked; break;
                case "30m": Preamp_30 = Preamp.Checked; break;
                case "20m": Preamp_20 = Preamp.Checked; break;
                case "17m": Preamp_17 = Preamp.Checked; break;
                case "15m": Preamp_15 = Preamp.Checked; break;
                case "12m": Preamp_12 = Preamp.Checked; break;
                case "10m": Preamp_10 = Preamp.Checked; break;
                case "6m": Preamp_6 = Preamp.Checked; break;
                case "GC": Preamp_GC = Preamp.Checked; break;
            }
            set_frequency.Focus(); // return focus to the frequency slider
            

        }

        // save the current band & frequency in a quick memory
        private void StoreFreq_Click(object sender, EventArgs e)
        {
            StoreFreq.BackColor = Color.Gray;
            QuickMemoryBand = BandSelect.Text;
            QuickMemory = set_frequency.Value;
            set_frequency.Focus(); // return focus to the frequency slider
        }

        // restore a previously stored quick band & frequency 
        private void RecallFreq_Click(object sender, EventArgs e)
        {
            RecallFreq.BackColor = Color.Gray;
            BandSelect.Text = QuickMemoryBand;
            set_frequency.Value = QuickMemory;
            Frequency_change();     // process the new frequency 
            set_frequency.Focus(); // return focus to the frequency slider
           
        }

        // process key strokes from the keyboard 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            bool display = false;
            Debug.WriteLine("got a key \t" + e.KeyCode);

            // process key presses from the numeric keypad
            switch (e.KeyCode)
            {
                case Keys.NumPad0: KeypadFrequency += "0"; keycount++; break;
                case Keys.NumPad1: KeypadFrequency += "1"; keycount++; break;
                case Keys.NumPad2: KeypadFrequency += "2"; keycount++; break;
                case Keys.NumPad3: KeypadFrequency += "3"; keycount++; break;
                case Keys.NumPad4: KeypadFrequency += "4"; keycount++; break;
                case Keys.NumPad5: KeypadFrequency += "5"; keycount++; break;
                case Keys.NumPad6: KeypadFrequency += "6"; keycount++; break;
                case Keys.NumPad7: KeypadFrequency += "7"; keycount++; break;
                case Keys.NumPad8: KeypadFrequency += "8"; keycount++; break;
                case Keys.NumPad9: KeypadFrequency += "9"; keycount++; break;
                case Keys.Back:
                    if (keycount > 0)  // remove the last character entered
                    {
                        KeypadFrequency = KeypadFrequency.Remove(KeypadFrequency.Length - 1);
                        --keycount;
                    }
                    break;
                case Keys.Enter:
                    if (keycount > 0)   // so we don't trigger on the main Enter Key 
                        display = true;
                    break;
                case Keys.Decimal: KeypadFrequency += "."; break;
            }

            // display the frequency as it is entered but only if entered on the keypad 
            if (keycount > 0)
            {
                display_freq.Text = KeypadFrequency;
                output_frequency = KeypadFrequency;
              //  create_connection();
            }

            float temp = 0.0f;
            int setfrequency;
            if (display)  // user has pressed enter key 
            {
                try  // data format may be incorrect so check
                {
                    temp = (float)Convert.ToDecimal(KeypadFrequency); // convert to a float 
                }

                catch // format error so tidy up and return
                {
                    Frequency_change();     // display the previous frequency
                    set_frequency.Focus(); // return focus to the frequency slider
                    KeypadFrequency = null;
                    keycount = 0;
                    return;
                }
                float temp2 = temp * 1000000.0F;  // Multiply by 10e6 to give Hz
                setfrequency = (int)temp2; // convert to an integer
                // check frequency is valid
                if (keycount > 0 && keycount < 9 && setfrequency < 55000001)
                {
                    Debug.WriteLine(setfrequency);
                    BandSelect.Text = getBand(setfrequency);
                    set_frequency.Value = setfrequency;
                    // display the new frequency and send to Ozy
                    DisplayFrequency(setfrequency);
                    KeypadFrequency = null;
                    keycount = 0;
                    set_frequency.Focus(); // return focus to the frequency slider
                    
                    return;
                }
                else  // error, abort
                {
                    Frequency_change();     // display the previous frequency
                    set_frequency.Focus(); // return focus to the frequency slider
                    KeypadFrequency = null;
                    keycount = 0;
                    return;
                }
            }
        }

        #endregion
        private void DisplayFrequency(int setfrequency)
        {
          

            // convert tune frequency to an array of bytes ready to send to Ozy
            frequency = BitConverter.GetBytes(set_frequency.Value);
            // format the integer frequency to have a decimal point between the MHz digits.
            float temp_freq = (float)setfrequency / 1000000.0f;
            // convert to a string with 6 digits to the right of the decimal point 
            string freq = String.Format(nfi, "{0:F6}", (float)temp_freq);
            // strip off the last 3 digits, add a space then add them back 
            display_freq.Text = freq.Remove(freq.Length - 3) + ' ' + freq.Substring(freq.Length - 3, 3);
            output_frequency = freq.Remove(freq.Length - 3) + ' ' + freq.Substring(freq.Length - 3, 3);

            create_connection();

            
        }

        #region

        // find out what band we are on when user enters frequency from keypad
        private string getBand(int frequency)
        {
            if (frequency > 1800000 && frequency < 2000000) return "160m";
            else if (frequency > 3500000 && frequency < 4000000) return "80m";
            else if (frequency > 7000000 && frequency < 7300000) return "40m";
            else if (frequency > 10100000 && frequency < 10150000) return "30m";
            else if (frequency > 14000000 && frequency < 14350000) return "20m";
            else if (frequency > 18068000 && frequency < 18168000) return "17m";
            else if (frequency > 21000000 && frequency < 21450000) return "15m";
            else if (frequency > 24880000 && frequency < 24980000) return "12m";
            else if (frequency > 28000000 && frequency < 29700000) return "10m";
            else if (frequency > 50000000 && frequency < 55000000) return "6m";
            return "GC";
        }

        private void numericUpDownAGCTime_ValueChanged(object sender, EventArgs e)
        {
            // get and display current time for this particular mode
            rcvr.AGCDecayTime = (float)Convert.ToDouble(numericUpDownAGCTime.Value);
        }

        private void setupToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (Setup_form == null || Setup_form.IsDisposed )  // only allow user to open form if not already open
            {
                Setup_form = new SetupForm(this);
                Setup_form.TopMost = true;          // have the setup form appear over the main form.
                Setup_form.SampleRate.Text = SampleRate.ToString(); // pass the current Sampling Rate 
                Setup_form.Owner = this;
                Setup_form.Show();                  // display the Setup form
                set_frequency.Focus();              // return focus to the frequency slider
                Setup_form.Set_CWPitch.Value = CWPitch; // setup the initial value of cw pitch control on the setup form
                Setup_form.PennyOC.Enabled = PennyOC; // set the status of Penny Open Collector controls
            }
            else  // form already open 
            {
                // handle setup form already being shown.  Make sure the user sees it...
                Setup_form.Show();                  // display the Setup form
                set_frequency.Focus();              // return focus to the frequency slider
            }
        }

        private void chkWaterFall_CheckedChanged(object sender, EventArgs e)
        {
            if (chkWaterFall.Checked)
            {
                // turning on...
                // init the waterfall data, causing the waterfallBitmap to be blanked...
                ClearWaterfall();

                chkWideSpec.Checked = false;
            }

            SetSplitterBar();

            set_frequency.Focus(); // return focus to the frequency slider
        }

        Image pictureBoxWidebandBackground = null;
        bool useWidebandBackground = false;
        private void pictureBoxWideband_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            if (useWidebandBackground)
            {
                if ((pictureBoxWidebandBackground == null) || (pictureBoxWideband.BackgroundImage == null) || updateWidebandGraphBackground)
                {
                    pictureBoxWideband.BackgroundImage = null;
                    DrawWidebandGrid(g);            // draw the background grid
                    pictureBoxWidebandBackground = pictureBoxWideband.Image;
                    pictureBoxWideband.BackgroundImage = pictureBoxWidebandBackground;
                    updateWidebandGraphBackground = false;
                    UpdateGraphBackgrounds();
                }
            }
            else
            {
                pictureBoxWideband.BackgroundImage = null;
            }

            if (chkWideSpec.Checked || chkWaterFall.Checked) // if a bandscope etc is selected then display it
                RefreshWidebandScope(g);
        }

        Image pictureBoxSpectrumBackground = null;
        bool useSpectrumBackground = false;
        private void pictureBoxSpectrum_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            if (useSpectrumBackground)
            {
                if ((pictureBoxSpectrumBackground == null) || (pictureBoxSpectrum.BackgroundImage == null) || updateSpectrumGraphBackground)
                {
                    pictureBoxSpectrum.BackgroundImage = null;
                    DrawSpectrumGrid(g);            // draw the background grid
                    pictureBoxSpectrumBackground = pictureBoxSpectrum.Image;
                    pictureBoxSpectrum.BackgroundImage = pictureBoxSpectrumBackground;
                    updateSpectrumGraphBackground = false;
                }
            }
            else
            {
                pictureBoxSpectrum.BackgroundImage = null;
            }

            if (chkSpec.Checked || ShowI || ShowQ) // if a bandscope etc is selected then display it
                RefreshSpectrumScope(g);
        }

        private void pictureBoxSpectrum_MouseDown(object sender, MouseEventArgs e)
        {
            // check if user has clicked over squelch level control rectangle, if so set SquelchDrag to true
            if (squelch.Contains(e.X, e.Y))  // this gives true if the mouse is inside the squelch rectangle
            {
                SquelchDrag = true;
                return;  // since we don't want any other controls active along with squench
            }           
            
            // check if we are adjacent to the right filter location, if so change the mouse cursor to <-> and 
            // disable mouse drag tuning of the frequency and filter shift
            if (Control.MouseButtons == MouseButtons.Left && MousePositionX > FilterRight - 5 && MousePositionX < FilterRight)
            {
                pictureBoxSpectrum.Cursor = Cursors.SizeWE; // change cursor to <->
                FrequencyDrag = false;   // turn off drag tuning of frequency and filter shift 
                BandwidthShift = false;
                HighFilter = true;      // allow drag tuning of high filter frequency
                LowFilter = false;
            }
            // check if we are adjacent to the left filter location, if so change the mouse cursor to <-> and 
            // disable mouse drag tuning of the frequency and filter shift
            else if (Control.MouseButtons == MouseButtons.Left && MousePositionX > FilterLeft && MousePositionX < FilterLeft + 5)
            {
                pictureBoxSpectrum.Cursor = Cursors.SizeWE; // change cursor to <->
                FrequencyDrag = false;      // turn off drag tuning of frequency and filter shift 
                BandwidthShift = false;
                HighFilter = false;
                LowFilter = true;           // allow drag tuning of low filter frequency
            }

            // if the mouse postion is to the left or right of the filter location then change the mouse cursor to a hand
            // and enable frequency drag. If we have right clicked then ShowCross will be true in which case set the frequency  
            // to the current mouse position.
            else if (Control.MouseButtons == MouseButtons.Left && (MousePositionX < FilterLeft || MousePositionX > FilterRight))
            {
                pictureBoxSpectrum.Cursor = Cursors.Hand; // change cursor to a hand

                FrequencyDrag = true;  // we are not inside the filter bandwidth so enable drag tuning

                // if the cross is shown then set frequency to the X location of the mouse
                if (ShowCross)
                {
                    // calculate the number of Hz each pixel represents, will be  sample rate/screen bandscope width
                    int HzPerPixel = SampleRate / pictureBoxSpectrum.Width;
                    // calculate the  how many Hz the X mouse location represents from the left edge of the screen
                    int offset = MousePositionX * HzPerPixel;
                    // the frequency at the left edge of the screen will be the tuned frequency - sample rate/2
                    int left_edge_frequency = set_frequency.Value - SampleRate / 2;
                    // add the offset to the left edge frequency
                    // do so in a way that does NOT generate an exception when going too low in frequency
                    if (left_edge_frequency + offset <= set_frequency.Minimum)
                    {
                        set_frequency.Value = set_frequency.Minimum;
                    }
                    else if (left_edge_frequency + offset >= set_frequency.Maximum)
                    {
                        set_frequency.Value = set_frequency.Maximum;
                    }
                    else
                    {
                        if (Mode.Text == "CWL")  // TODO: Hack so that click tune on CWL mode works - need to fix
                            set_frequency.Value = left_edge_frequency + offset + 1000;
                        else
                            set_frequency.Value = left_edge_frequency + offset;
                    }
                    // display the new frequency and send to Ozy
                    DisplayFrequency(set_frequency.Value);
                    set_frequency.Focus(); // return focus to the frequency slider
                }
            }
            // if the right mouse button is down then show large cross 
            else if (Control.MouseButtons == MouseButtons.Right)  // change the cursor to to a large cross
            {
                pictureBoxSpectrum.Cursor = Cursors.Cross;         // change cursor to a small cross
                // set the ShowCross flag, this is read in the DrawGrid() Method
                ShowCross = !ShowCross;                     // toggle the cross each right click
                FrequencyDrag = true;
            }

            else // we are inside the filter bandwidth so disable frequency drag tuning
            {
                FrequencyDrag = false;
                pictureBoxSpectrum.Cursor = Cursors.NoMoveHoriz;
            }
        }

        private void pictureBoxSpectrum_MouseEnter(object sender, EventArgs e)
        {
            pictureBoxSpectrum.Cursor = Cursors.Cross;
        }

        private void pictureBoxSpectrum_MouseLeave(object sender, EventArgs e)
        {
            pictureBoxSpectrum.Cursor = Cursors.Default;
        }


        int mouse_delta_Y;
        private void pictureBoxSpectrum_MouseMove(object sender, MouseEventArgs e)
        {
            // get the current cursor shape so we can restore it upon leaving 
            Cursor CurrentCursor = pictureBoxSpectrum.Cursor;

            MousePositionX = e.X;   // get the mouse X and Y coordintes with respect to the bandscope
            MousePositionY = e.Y;   // these are used by the right mouse click over bandscope             
            Point MouseX = new Point();  // the mouse coordinates have a type Point
            MouseX = Control.MousePosition;  // get current mouse postion (X,Y) with respect to the screen
                                             // so we can drag tune outside the width of the bandscope area 
            delta = MouseOld.X - MouseX.X;  // get the difference between the last and current Mouse X coordinate
            int abs_delta = Math.Abs(delta);
            mouse_delta_Y = MouseOld.Y - MouseX.Y; // get the diffetence between the last and current Mouse Y coordinate
            int abs_mouse_delta_Y = Math.Abs(mouse_delta_Y);

            // check if the user has clicked on the squelch control rectangle, if so move the squelch threshold with the mouse
            if (SquelchDrag)
            {
                if (mouse_delta_Y < 0 )  // mouse has moved down 
                {     
                    if (Squelch_level.Value/10 > (GridMin + abs_mouse_delta_Y)) // check we can make a valid move
                        Squelch_level.Value -= abs_mouse_delta_Y * (512 * 3/pictureBoxSpectrum.Height);
                    //  512 * 3/pictureBoxSpectrum.Height is a magic number to get the line to move at a nice speed!
                }

                else if (mouse_delta_Y > 0) // mouse has moved up
                {
                    if(Squelch_level.Value/10 < (GridMax - abs_mouse_delta_Y))  // check we can make a valid move
                        Squelch_level.Value += abs_mouse_delta_Y * (512 * 3 / pictureBoxSpectrum.Height);
                }

                // update text box
                Squelch_level_Scroll(this, EventArgs.Empty);
                MouseOld = MouseX; // save the last mouse X location for next time the mouse moves
                return;  // so other mouse controls don't operate 
            }           

            if (Control.MouseButtons == MouseButtons.Left && FrequencyDrag) // do this if the left mouse button is down
            {
                // if mouse has moved left then decrease frequency but first 
                // we need to check if we are too close to the band edges to move 
                if (delta < 0)
                {
                    if (set_frequency.Value > (set_frequency.Minimum + abs_delta * (SampleRate / pictureBoxSpectrum.Width)))
                        set_frequency.Value -= abs_delta * (SampleRate / pictureBoxSpectrum.Width);
                }
                // if mouse has moved right then decrease frequency but first
                // we need to check if we are too close to the band edges to move  
                else if (delta > 0)
                {
                    if (set_frequency.Value < (set_frequency.Maximum - abs_delta * (SampleRate / pictureBoxSpectrum.Width)))
                        set_frequency.Value += abs_delta * (SampleRate / pictureBoxSpectrum.Width);
                }

                DisplayFrequency(set_frequency.Value);

                set_frequency.Focus(); // return focus to the frequency slider
            }

            // Allow user to alter  filter  high and low frequencies using mouse.
            // If the mouse is in the region of the left edge of the filter allow it to be drag tuned
            // Placing the mouse in this region will alter the cursor, if the left mouse button is
            // down then the filter can be altered by draging the mouse left or right.
            // If the user has pressed the left mouse button whilst in this region then LowFilter will be true

            else if (MousePositionX > FilterLeft && MousePositionX < FilterLeft + 5 || LowFilter)
            {
                pictureBoxSpectrum.Cursor = Cursors.SizeWE;            // change cursor to <->
                if (Control.MouseButtons == MouseButtons.Left)
                {
                    BandwidthShift = false; // turn off bandwith shifting with mouse
                    if (delta > 0) // then we are draging to the left  and delta is positive
                    {
                        if (Mode.Text == "LSB" || Mode.Text == "CWL")
                        {
                            if (rcvr.FilterFrequencyHigh > -9999.0f)
                                rcvr.FilterFrequencyHigh -= delta * 100;
                        }
                        else
                        {
                            if (rcvr.FilterFrequencyLow > -9999.0f)
                                rcvr.FilterFrequencyLow -= delta * 100;
                        }
                    }
                    if (delta < 0) // then we are draging to the right  and delta is negative
                    {
                        if (Mode.Text == "LSB" || Mode.Text == "CWL")
                        {
                            if (rcvr.FilterFrequencyHigh < 9999.0f)
                                rcvr.FilterFrequencyHigh -= delta * 100;
                        }
                        else
                        {
                            if (rcvr.FilterFrequencyLow < 9999.0f)
                                rcvr.FilterFrequencyLow -= delta * 100;
                        }
                    }
                }
            }

            // If the mouse is in the region of the right edge of the filter allow it to be drag tuned
            // Placing the mouse in this region will alter the cursor, if the left mouse button is
            // down then the filter can be altered by draging the mouse left or right.
            // If the user has pressed the left mouse button whilst in this region then HighFilter will be true
            else if (MousePositionX > FilterRight - 5 && MousePositionX < FilterRight || HighFilter)
            {
                pictureBoxSpectrum.Cursor = Cursors.SizeWE;        // change cursor to <->
                if (Control.MouseButtons == MouseButtons.Left)
                {
                    BandwidthShift = false; // turn off bandwith shifting with mouse
                    if (delta > 0) // then we are draging to the left and delta is positive
                    {
                        if (Mode.Text == "LSB" || Mode.Text == "CWL") // need to invert if so
                        {
                            if (rcvr.FilterFrequencyLow > -9999.0f)
                                rcvr.FilterFrequencyLow -= delta * 100;
                        }
                        else
                        {
                            if (rcvr.FilterFrequencyHigh > -9999.0f)
                                rcvr.FilterFrequencyHigh -= delta * 100;
                        }

                    }
                    if (delta < 0) // then we are dragging to the right and delta is negative
                    {
                        if (Mode.Text == "LSB" || Mode.Text == "CWL") // need to invert if so
                        {
                            if (rcvr.FilterFrequencyLow < 9999.0f)
                                rcvr.FilterFrequencyLow -= delta * 100;
                        }
                        else
                        {
                            if (rcvr.FilterFrequencyHigh < 9999.0f)
                                rcvr.FilterFrequencyHigh -= delta * 100;
                        }
                    }

                }
            }

            // If the mouse is over the filter bandwith  change the cursor. If the left mouse button is also down then 
            // drag tune the filter frequency i.e passband tuning
            else if (MousePositionX > FilterLeft + 5 && MousePositionX < FilterRight - 5 || !FrequencyDrag && BandwidthShift)
            {
                pictureBoxSpectrum.Cursor = Cursors.NoMoveHoriz;

                if (Control.MouseButtons == MouseButtons.Left && !FrequencyDrag) // and if the left mouse is down move filter
                {
                    // determine how wide the current filter is
                    int FilterWidth = Math.Abs((int)(rcvr.FilterFrequencyLow - rcvr.FilterFrequencyHigh));
                    {
                        if (delta < 0)  // then we are draging to the right and delta is negative
                        {
                            if (Mode.Text == "LSB" || Mode.Text == "CWL")
                            {
                                if (rcvr.FilterFrequencyLow < 9999.0f)  // only drag to the maximum bandwidth
                                {
                                    rcvr.FilterFrequencyLow -= delta * 100;
                                    rcvr.FilterFrequencyHigh = rcvr.FilterFrequencyLow - FilterWidth;
                                }
                            }
                            else
                            {
                                if (rcvr.FilterFrequencyHigh < 9999.0f)  // only drag to the maximum bandwidth
                                {
                                    rcvr.FilterFrequencyLow -= delta * 100;
                                    rcvr.FilterFrequencyHigh = rcvr.FilterFrequencyLow + FilterWidth; // all other modes
                                }
                            }
                        }
                        if (delta > 0)  // then we are draging to the left and delta is positive
                        {

                            if (Mode.Text == "LSB" || Mode.Text == "CWL")
                            {
                                if (rcvr.FilterFrequencyHigh > -9999.0f) // only drag to the maximum bandwidth
                                {
                                    rcvr.FilterFrequencyHigh -= delta * 100;
                                    rcvr.FilterFrequencyLow = rcvr.FilterFrequencyHigh + FilterWidth;
                                }
                            }
                            else
                            {
                                if (rcvr.FilterFrequencyLow > -9999.0f) // only drag to the maximum bandwidth
                                {
                                    rcvr.FilterFrequencyHigh -= delta * 100;
                                    rcvr.FilterFrequencyLow = rcvr.FilterFrequencyHigh - FilterWidth;
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // if the mouse is over the squelch control rectangle then change mouse icon to an up/down  arrow
                if (squelch.Contains(e.X, e.Y))
                    pictureBoxSpectrum.Cursor = Cursors.HSplit;

                else pictureBoxSpectrum.Cursor = Cursors.Cross;  // restore cursor 
            }

            MouseOld = MouseX; // save the last mouse X location for next time the mouse moves
        }
      
        private void pictureBoxSpectrum_MouseUp(object sender, MouseEventArgs e)
        {
            pictureBoxSpectrum.Cursor = Cursors.Cross;
            FrequencyDrag = true;
            BandwidthShift = true;
            HighFilter = false;
            LowFilter = false;
            SquelchDrag = false; 
        }

        private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
        {
            UpdateGraphBackgrounds();
        }

        // show the squelch level the user has set
        private void Squelch_level_Scroll(object sender, EventArgs e)
        {
            Squelch_setting.Text = (Squelch_level.Value/10).ToString();
        }

        private bool squelchOn = false;

        private void Bandscope_squelch_CheckedChanged(object sender, EventArgs e)
        {
            if (Bandscope_squelch.Checked)
            {
                if (Filter_squelch.Checked)
                    Filter_squelch.Checked = false;

                squelchOn = true;
            }
            else
            {
                if (!Filter_squelch.Checked)
                {
                    squelchOn = false;
                    setVolumeSquelched();
                }
            }
            set_frequency.Focus(); // return focus to the frequency slider
        }

        private void Filter_squelch_CheckedChanged(object sender, EventArgs e)
        {
            if (Filter_squelch.Checked)
            {
                if (Bandscope_squelch.Checked)
                    Bandscope_squelch.Checked = false;

                squelchOn = true;
            }
            else
            {
                if (!Bandscope_squelch.Checked)
                {
                    squelchOn = false;
                    setVolumeSquelched();
                }
            }        
            set_frequency.Focus();                      // return focus to the frequency slider
        }
        
        // set the drive level to Penelope 
        public void DriveLevel_Scroll(object sender, EventArgs e)
        {
            Drive.Text = DriveLevel.Value.ToString();
            DriveGain = (float)DriveLevel.Value / DriveLevel.Maximum;  // DriveGain is float from 0 to 1.0
            Debug.WriteLine("DriveGain = \t " + DriveGain);
            set_frequency.Focus();                              // return focus to the frequency slider
        }
       
        // Toggle the MOX button 
        private void MOX_Click(object sender, EventArgs e)
        {
            if (KK_on)                                          // only allow MOX to be set when radio is on 
            {  
                if (MOX.BackColor == SystemColors.Control)
                {
                    MOX.BackColor = Color.Green;
                    MOX_set = true;
                }
                else
                {
                    MOX.BackColor = SystemColors.Control;
                    MOX_set = false;
                    if (TUN_set)
                    {
                        TUN.BackColor = SystemColors.Control;
                        TUN_set = false;
                        DriveLevel.Value = PreviousTune;    // restore the previous drive level
                    }
                }                
            }
            DriveLevel_Scroll(this, EventArgs.Empty);       // force drive level update
            setVolumeSquelched();                           // check if we need to mute the receiver audio
            set_frequency.Focus();                          // return focus to the frequency slider
        }

        // Toggle the TUN (Tune) button
        int PreviousTune;
        private void TUN_Click(object sender, EventArgs e)
        {
            if (KK_on) // only allow TUN to be set when radio is on 
            {
                if (TUN.BackColor == SystemColors.Control)
                {
                    TUN.BackColor = Color.Green;
                    TUN_set = true;
                    PreviousTune = DriveLevel.Value;
                    DriveLevel.Value = TuneLevel;       // keep the current drive level so we can restore it
                    MOX_set = true;                     // Need MOX on when Tune is set 
                    MOX.BackColor = Color.Green;
                }
                else
                {
                    TUN.BackColor = SystemColors.Control;
                    TUN_set = false;
                    DriveLevel.Value = PreviousTune;   // restore the previous drive level
                    MOX_set = false;                   // force MOX off 
                    MOX.BackColor = SystemColors.Control;
                }
            }
            DriveLevel_Scroll(this, EventArgs.Empty);   // force drive level update
            set_frequency.Focus();                      // return focus to the frequency slider
        }

        
        private void OnOffButton_Click(object sender, EventArgs e)
        {
            if (OnOffButton.BackColor == SystemColors.Control)
            {
                OnOffButton.BackColor = Color.Green;
                OnOffButton.Text      = "ON";
                KK_on                 = true;
                Start();
                set_frequency.Focus();                      // set focus to the frequency slider
            }
            else
            {
                OnOffButton.BackColor = SystemColors.Control;
                OnOffButton.Text      = "OFF";
                TUN_set               = false;              // force Tune off
                PTT                   = false;              // force PTT off
                MOX_set               = false;              // force MOX off
                previous_OnlyTxOnPTT  = OnlyTxOnPTT;        // save this status so we can restore it
                OnlyTxOnPTT           = true;               // force always PTT off 
                TUN.BackColor         = SystemColors.Control;
                DriveLevel.Value      = PreviousTune;       // restore the previous drive level
                DriveLevel_Scroll(this, EventArgs.Empty);   // force drive level update in case TUN is on                
                MOX.BackColor         = SystemColors.Control;                           
                Thread.Sleep(100);                          // Delay so the PTT off is sent to Ozy
                Stop();
                OnlyTxOnPTT = previous_OnlyTxOnPTT;         // restore the previous status 
                KK_on = false;                              // set this last so PTT off gets sent
            }
        }
        #endregion

        //receive the band and call sign values from SDT
        private void set_SDT_Values()
        {

            Socket ServerSocket = tcpListener.AcceptSocket();
            IPHostEntry IPHost = Dns.GetHostEntry(Dns.GetHostName());

            
            if (ServerSocket.Connected)
            {
                NetworkStream networkStream = new NetworkStream(ServerSocket);
                System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
                receive = "";
                String aa = "";

                //read all message that is send from SDT
                while (!streamReader.EndOfStream)
                {
                    aa = "";
                    aa = streamReader.ReadLine();
                }

                //receive hold the incoming message from SDT
                receive = aa;

               
                streamReader.Close();
                networkStream.Close();
            }
            //
            if (receive != null)
            {
                IsGetBand = true;
                IsChanged = true;
               
            }

            //parse the received message
            ParseStream(receive);
            //close the server
            ServerSocket.Close();


        }


        //method for parsing incoming SDT message
        private void ParseStream(String input)
        {
            char[] delimiter = { '$' };
            String input_band = "";
            String input_signcall = "";

          //split incoming message into string array
            String[] holder = receive.Split(delimiter);
           
            //input_band keeps the band value
            input_band = holder[0];

            //input_band keeps the callsign value
            input_signcall = holder[1];

            //global value temp is hold the band value
            temp = input_band;

        }

// Methos for seting band value of KK
        private void set_band(String input)
        {

            int input_Numval = 0;
            string band_val = "";

            input_Numval = Convert.ToInt32(input);

            if (input_Numval == 10)
            { band_val = "10m"; }
            else if (input_Numval == 12)
            { band_val = "12m"; }
            else if (input_Numval == 15)
            { band_val = "15m"; }
            else if (input_Numval == 17)
            { band_val = "17m"; }
            else if (input_Numval == 20)
            { band_val = "20m"; }
            else if (input_Numval == 30)
            { band_val = "30m"; }
            else if (input_Numval == 40)
            { band_val = "40m"; }
            else if (input_Numval == 80)
            { band_val = "80m"; }
            else if (input_Numval == 160)
            { band_val = "160m"; }
            else if (input_Numval == 6)
            { band_val = "6m"; }

            
            BandSelect.Text = band_val;
            
           
        }


        // Create_Connection Method

        private void create_connection()
        {


            String ip = "";
            IPHostEntry IPHost = Dns.GetHostEntry(Dns.GetHostName());
            ip = IPHost.AddressList[0].ToString();

            // tcp client socket for connecting SDT
            TcpClient clientSocket;
            try
            {
                clientSocket = new TcpClient(ip, 4445);
            }
            catch
            {

                Console.WriteLine("Failed to connect SDT");
                return;
            }

               // set the out_msg to frequency
                    String out_msg = "";
                    out_msg = output_frequency;

                    NetworkStream networkStream = clientSocket.GetStream();
                    System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
                   
                  //send to frequency to SDT
                    try
                    {
                        String outputString; //The String to be sent
                        {
                            outputString = out_msg;
                            streamWriter.WriteLine(outputString);
                            streamWriter.Flush();
                        }
                    }


                    catch (Exception ex)
                    {
                        Console.WriteLine("An exception occured: " + ex.ToString());
                    }

                    
                  networkStream.Close();
                  clientSocket.Close();

        }


        private void SdtFrequencyUpdate_Tick(object sender, EventArgs e)
        {
            // this is control loop for getting sdt values and the set the band for KK
            while (IsGetBand == false)
            {
                //First get the sdt values
                set_SDT_Values();

                //set the kk band
                set_band(temp);
                //send the frequency after set band
                create_connection();

            }

        }
        public void InitializeClientSidePiping()
        {
            NamedPipeClientStream pipeClient =
                    new NamedPipeClientStream(".", "kissPipe", // lolz
                        PipeDirection.InOut, PipeOptions.None,
                        TokenImpersonationLevel.Impersonation);
            pipeClient.Connect();

            StreamString ss = new StreamString(pipeClient);
            // Validate the server's signature string
            if (ss.ReadString() == "I am the one true server!")
            {
                // Start reading the FlexRadio data here
                // Note: this may need to be contained in a loop, in a seperate thread
                // so that it can continuously read data, as long as the KISS Konsole
                // and Workstation Launcher are both open. If this is modified, then the
                // Workstation Launcher code should be modified to reflect the changes
                String flexData = ss.ReadString();
            }
            else
            {
                Console.WriteLine("Server could not be verified.");
            }
        }
    }
}